pax_global_header00006660000000000000000000000064147657517210014531gustar00rootroot0000000000000052 comment=b33e3b4f589ead1dba3e524307c04141fcd96e80 vuos-0.9.2/000077500000000000000000000000001476575172100125355ustar00rootroot00000000000000vuos-0.9.2/CMakeLists.txt000066400000000000000000000051531476575172100153010ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) project("vuos" VERSION 0.9.2 DESCRIPTION "" HOMEPAGE_URL "https://github.com/virtualsquare/vuos" LANGUAGES C) include(GNUInstallDirs) include(CheckIncludeFile) include(CheckSymbolExists) set(LIBS_REQUIRED stropt volatilestream execs vdeplug fuse3) set(HEADERS_REQUIRED stropt.h strcase.h volatilestream.h execs.h libvdeplug.h fuse3/fuse.h pthread.h) # DFUSE_USE_VERSION 300 is just to test fuse3-dev availability set(CMAKE_REQUIRED_DEFINITIONS -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=300) foreach(THISLIB IN LISTS LIBS_REQUIRED) find_library(LIB${THISLIB}_OK ${THISLIB}) if(NOT LIB${THISLIB}_OK) message(FATAL_ERROR "library lib${THISLIB} not found") endif() endforeach(THISLIB) foreach(HEADER IN LISTS HEADERS_REQUIRED) check_include_file(${HEADER} ${HEADER}_OK) if(NOT ${HEADER}_OK) message(FATAL_ERROR "header file ${HEADER} not found") endif() endforeach(HEADER) unset(CMAKE_REQUIRED_DEFINITIONS) set(CMAKE_C_FLAGS "-ggdb -Wall -Wextra -pedantic -std=gnu11 -Wno-unused-parameter -O2 -D_FORTIFY_SOURCE=2") add_definitions(-D_GNU_SOURCE) set(MODULES_INSTALL_PATH ${CMAKE_INSTALL_FULL_LIBDIR}/vu/modules) set(CONFIGURATION_DIR_PATH ${CMAKE_INSTALL_FULL_SYSCONFDIR}) set(HEADERS_INSTALL_PATH ${CMAKE_INSTALL_FULL_INCLUDEDIR}) set(VU_HEADERS ${PROJECT_SOURCE_DIR}/include) set(VU_DYN_SOURCE_PATH ${CMAKE_BINARY_DIR}/umvu_dynsrc) set(VU_DYN_HEADER_PATH ${CMAKE_BINARY_DIR}/include) set(VU_SYSCALL_DEFS ${VU_DYN_HEADER_PATH}/syscall_defs.h) set(R_TABLE_H ${VU_DYN_HEADER_PATH}/r_table.h) set(SYSCALL_NR_COMPAT_H ${VU_DYN_HEADER_PATH}/syscall_nr_compat.h) set(VU_SYSNAMES ${VU_DYN_SOURCE_PATH}/syscall_names.c) set(VU_ARCHTABLE ${VU_DYN_SOURCE_PATH}/arch_table.c) set(VU_SYSTABLE ${VU_DYN_SOURCE_PATH}/syscall_table.c) set(VU_DYN_SOURCES ${VU_SYSNAMES} ${VU_ARCHTABLE} ${VU_SYSTABLE}) execute_process(COMMAND mkdir -p ${VU_DYN_SOURCE_PATH}) foreach(DYN ${VU_DYN_SOURCES}) execute_process(COMMAND touch -d 20000101 ${DYN}) endforeach(DYN) configure_file( ${VU_HEADERS}/config.h.in ${VU_DYN_HEADER_PATH}/config.h ) add_subdirectory(scripts) add_subdirectory(umvu) add_subdirectory(cmd) add_subdirectory(test_modules) add_subdirectory(libvumod) add_subdirectory(vufuse) add_subdirectory(vudev) add_subdirectory(vudevfuse) add_subdirectory(include) add_subdirectory(vunet) add_subdirectory(vufs) add_subdirectory(vubinfmt) add_subdirectory(vumisc) add_subdirectory(vunet_modules) add_subdirectory(vufuse_modules) add_subdirectory(vudev_modules) add_subdirectory(vumisc_modules) add_subdirectory(man) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_SOURCE_DIR}/Uninstall.cmake") vuos-0.9.2/COPYING000066400000000000000000000354371476575172100136040ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 Library 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 vuos-0.9.2/COPYING.LIB000066400000000000000000000636421476575172100142100ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! vuos-0.9.2/README.md000066400000000000000000000327501476575172100140230ustar00rootroot00000000000000# VUOS: give your processes a new VU # VUOS is a Virtual Operating System implemented at user space. Currently it implements about 150 Linux-compatible system calls providing support for a wide range of applications. Each process or even each thread in VUOS can see a different execution environment: file system contents, networking, devices, user ids etc. The main idea behind VUOS is that it is possible to give processes their own "view" using partial virtual machines. VUOS is a different perspective on namespaces, anykernels and related concepts. A partial virtual machine intercepts the system call requests and operates like a filter: system call can be forwarded to the kernel of the hosting system or processed by the partial virtual machine hypervisor. ``` Processes v +------------------+ | PSV hypervisor | --> virtualizing modules +------------------+ v (linux) kernel ``` In this way processes can see a *mix* of resources provided by the kernel (on which they have the same *view* of the other processes) and virtual resource. It is possible to mount filesystems, load networking stack, change the structure of the file system tree, create virtual devices. The hypervisor is just a user process so while it gives new perspective for processes, **it does not widen the attack surface of the kernel**. ## Some examples ## ... just to show something VUOS is capable of. NB: VUOS is much much more than this, and it is under active developmemnt. ### mount a file system image (using fuse virtual device) ### This example uses **umvu**: a user-mode implementation of the VUOS concepts based on ptrace. In the future VUOS could be re-implemented on other tracing/virtualizing supports. start the hypervisor, and run a bash *inside* the partial virtual machine $ umvu bash This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference $ PS1='\$\$ ' let us load vufuse: a user-mode implementation of FUSE (source compatible with FUSE modules) $$ vu_insmod fuse nothing is currently mounted on /mnt $$ ls /mnt run the FUSE handler program (it uses the virtual /dev/fuse) $$ fuse-ext2 -o ro /tmp/linux.img /mnt now the image has been mounted: $$ ls /mnt bin boot dev etc lib lost+found mnt proc sbin tmp usr $$ vuumount /mnt $$ ls /mnt $$ exit We have left the partial virtual machine Comments: user can *mount* any filesystem they like, on any directory. The linux kernel is not involved for all the system calls related to files in the mounted filesystem. The effects of this *mount* is just *perceived* by the processes running in the partial virtual machine. `vumount` is just a wrapper to the `mount(1)` system call (the command `mount(8)` does much much more, it is setuid root and requires real uid to be root to permit filesystem mounting (`mount(8)` works in `umvu` adding a module of uid/gid virtualization). ### mount a file system image (using vufuse) ### start the hypervisor, and run a bash *inside* the partial virtual machine $ umvu bash This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference $ PS1='\$\$ ' let us load vufuse: a user-mode implementation of FUSE (source compatible with FUSE modules) $$ vu_insmod vufuse nothing is currently mounted on /mnt $$ ls /mnt the following command mounts the filesystem image /tmp/linux.img $$ vumount -t vufuseext2 -o ro /tmp/linux.img /mnt now the image has been mounted: $$ ls /mnt bin boot dev etc lib lost+found mnt proc sbin tmp usr $$ vuumount /mnt $$ ls /mnt $$ exit We have left the partial virtual machine ### create a disk image, partition it, create a filesystem and mount it ### start the hypervisor, and run a bash *inside* the partial virtual machine $ umvu bash This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference $ PS1='\$\$ ' let us load vudev and fuse: vudev to virtualize devices and fuse as in the previous example $$ vu_insmod vudev fuse Note: it is possible to use vufuse instead of fuse. the command is `vu_insmod vudev vufuse`. create a 1 GiB large empty file $$ truncate -s 1G /tmp/disk $$ ls -l /tmp/disk -rw-r--r-- 1 renzo renzo 1073741824 Jun 3 11:55 /tmp/disk let us mount the empty file as a partitioned virtual disk: $$ vumount -t vudevpartx /tmp/disk /dev/hda Bad MBR signature 0 0 clearly if not a partitioned disk, yet. Let us add a partitioning scheme: $$ /sbin/gdisk /dev/hda GPT fdisk (gdisk) version 1.0.3 Partition table scan: MBR: not present BSD: not present APM: not present GPT: not present Creating new GPT entries. Command (? for help): n Partition number (1-128, default 1): First sector (34-2097118, default = 2048) or {+-}size{KMGTP}: Last sector (2048-2097118, default = 2097118) or {+-}size{KMGTP}: +200M Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem' Command (? for help): n Partition number (2-128, default 2): First sector (34-2097118, default = 411648) or {+-}size{KMGTP}: Last sector (411648-2097118, default = 2097118) or {+-}size{KMGTP}: Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem' Command (? for help): p Disk /dev/hda: 2097152 sectors, 1024.0 MiB Sector size (logical): 512 bytes Disk identifier (GUID): F2A76123-73ED-4052-BAFE-6B37473E6187 Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 2097118 Partitions will be aligned on 2048-sector boundaries Total free space is 2014 sectors (1007.0 KiB) Number Start (sector) End (sector) Size Code Name 1 2048 411647 200.0 MiB 8300 Linux filesystem 2 411648 2097118 823.0 MiB 8300 Linux filesystem Command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): Y OK; writing new GUID partition table (GPT) to /dev/hda. The operation has completed successfully. The disk has been partitioned: $$ ls -l /dev/hda1 brw------- 0 renzo renzo 0, 1 Jan 1 1970 /dev/hda1 $$ ls -l /dev/hda2 brw------- 0 renzo renzo 0, 2 Jan 1 1970 /dev/hda2 Now it is possible to create an ext4 partition on /dev/hda1 $$ /sbin/mkfs.ext4 /dev/hda1 mke2fs 1.45.1 (12-May-2019) warning: Unable to get device geometry for /dev/hda1 Creating filesystem with 204800 1k blocks and 51200 inodes Filesystem UUID: c96c6499-40cd-43df-addf-52e06d7e6842 Superblock backups stored on blocks: 8193, 24577, 40961, 57345, 73729 Allocating group tables: done Writing inode tables: done Creating journal (4096 blocks): done Writing superblocks and filesystem accounting information: done now the file system on /dev/hda1 can be mounted on /mnt $$ fuse-ext2 -o rw+ /dev/hda1 /mnt Note: the mount command for vufuse instead of fuse is `vumount -t vufuseext2 -o rw+ /dev/hda1 /mnt` add a significative file on /mnt $$ echo ciao > /mnt/hello $$ ls -l /mnt total 13 -rw-r--r-- 1 renzo renzo 5 Jun 3 12:09 hello drwx------ 2 root root 12288 Jun 3 12:06 lost+found $$ vuumount /mnt $$ vuumount /dev/hda $$ exit $ ### mount a user-level networking stack ### It is possible to provide network partial virtualization using the `vunet` module start the hypervisor, and run a bash *inside* the partial virtual machine $ umvu bash This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference $ PS1='\$\$ ' let us load vunet $$ vu_insmod vunet the following command *mounts* a vde network on /dev/net/myvde using libioth. (see https://github.com/rd235/vdeplug4) (any ioth supported stack can be used. The mount source argument is the stack implementation to use, vdestack in this example). $$ vumount -t vunetioth -o vxvde:// vdestack /dev/net/myvde Alternatively: the following command uses a vunet specific implementation of vdestack: $$ vumount -t vunetvdestack vxvde:// /dev/net/myvde vustack is the command to select the stack to use. $$ vustack /dev/net/myvde ip link 1: lo: mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff without vustack I can still access the stack provided by the linux kernel $$ ip link 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 80:aa:bb:cc:dd:ee brd ff:ff:ff:ff:ff:ff let us start a bash using /dev/net/myvde as itsdfault net $$ vustack /dev/net/myvde bash $ PS1='\$N\$ ' let us configure the net $N$ ip addr add 192.168.250.250/24 dev vde0 $N$ ip link set vde0 up $N$ ip route add default via 192.168.250.1 $N$ ip addr 1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff inet 192.168.250.250/24 scope global vde0 valid_lft forever preferred_lft forever inet6 fe80::7c76:c0ff:fed7:3b37/64 scope link valid_lft forever preferred_lft forever $N$ ip route default via 192.168.250.1 dev vde0 192.168.250.0/24 dev vde0 proto kernel scope link src 192.168.250.250 $N$ ping 80.80.80.80 PING 80.80.80.80 (80.80.80.80) 56(84) bytes of data. 64 bytes from 80.80.80.80: icmp_seq=1 ttl=52 time=56.9 ms 64 bytes from 80.80.80.80: icmp_seq=2 ttl=52 time=57.9 ms ^C $N$ ## Structure of umvu ## `umvu` has a three layer architecture: * core `umvu` hypervisor * modules (e.g. `vufuse, vunet, vufs, vudev`) * submodules (e.g. vufuseext2, vudevpartx, vudevnull, vunetvdestack `umvu` traces all the system call requests generated by the processes *and by the modules* and decides if the request is *real* or *virtual* and in this latter case which is the module to reroute the request. Absolute pathnames, file descriptors, family of protocols, ioctl tags, syscall number can be used to select the right module. Modules register their `boundary of responsibility` to the core hypervisor: i.e. which path prefixes, file descriptors, etc. they are responsible for. The API of modules consists of a subset of the system call API. When a process uses a read system call on a virtualized file (e.g. a file in a vumounted partition), the corresponding module receives a read request having the same signature of the standard system call. As an example the test module test\_modules/unreal.c, provides a *view* of the entire file system in `/unreal` and in `/unreal/unreal` simply using the system calls as module methods. (e.g. the function to implement lstat in the module is lstat, and so on. The only two function that had to be defined were: `getdents64` as gliibc does not provide an interface to it and `access` as it lacks a `flags` argument). The API between modules and submodules is tailored to the specific requirements. The API for filesystems has been chose to provide source level compatibility with FUSE modules. ## Installing umvu ## In order to test umvu several libraries and helper tools are required. The tests here above have been run on debian sid. For the sake of compleness (and hopefully clarity), it is possible to install all the code by hand, step by step as briefly explained in the following. First of all install the following packets: git python3 build-essential cmake make autogen autoconf libtool libcap-dev libattr1-dev libfuse-dev libexecs-dev libssl1.0-dev libmhash-dev libpam0g-dev libfuse-dev e2fsprogs comerr-dev e2fslibs-dev libpam-dev libmhash-dev Then install libraries and tools from the following list of git repositories: https://github.com/rd235/strcase.git https://github.com/virtualsquare/vde-2.git https://github.com/rd235/vdeplug4.git https://github.com/virtualsquare/purelibc.git https://github.com/rd235/libvolatilestream.git https://github.com/rd235/libstropt.git https://github.com/rd235/libfduserdata.git https://github.com/rd235/libvpoll-eventfd.git https://github.com/rd235/libvdestack.git https://github.com/rd235/vdeplug_vlan.git https://github.com/rd235/cado.git https://github.com/alperakcan/fuse-ext2.git https://github.com/rd235/vdeplug_agno.git https://github.com/rd235/vdens.git https://github.com/virtualsquare/libioth.git https://github.com/virtualsquare/vuos.git A symbolic link is required to make vufuseext2 reachable in the right dir ln -s /usr/local/lib/umview/modules/umfuseext2.so /usr/local/lib/vu/modules/vufuseext2.so vuos-0.9.2/Uninstall.cmake000066400000000000000000000010361476575172100155100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(NOT EXISTS ${MANIFEST}) message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'") endif() file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(EXISTS ${file} OR IS_SYMLINK ${file}) message(STATUS "Removing: ${file}") execute_process( COMMAND rm -f ${file} RESULT_VARIABLE retcode ) if(NOT "${retcode}" STREQUAL "0") message(WARNING "Failed to remove: ${file}") endif() endif() endforeach(file) vuos-0.9.2/cmd/000077500000000000000000000000001476575172100133005ustar00rootroot00000000000000vuos-0.9.2/cmd/CMakeLists.txt000066400000000000000000000010711476575172100160370ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VU_CMDS ${CMAKE_CURRENT_SOURCE_DIR}/*.c) foreach(VU_CMD ${VU_CMDS}) string(REPLACE ".c" "" VU_CMD_FILE ${VU_CMD}) get_filename_component(VU_CMD_TARGET ${VU_CMD_FILE} NAME) add_executable(${VU_CMD_TARGET} ${VU_CMD}) install(TARGETS ${VU_CMD_TARGET} RUNTIME DESTINATION bin) endforeach(VU_CMD) target_link_libraries(vumount stropt) target_link_libraries(vustack stropt) vuos-0.9.2/cmd/vu_insmod.c000066400000000000000000000037121476575172100154520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include static char *progname; void usage() { fprintf(stderr, "Usage:\n" " %s OPTIONS vu_module [vu_module] ...\n" " OPTIONS:\n" " -p --permanent: rmmod forbidden\n\n" " -h --help: print this help message\n\n", progname); exit(2); } static const char *short_options = "p"; static const struct option long_options[] = { {"permanent",0,0,'p'}, {"help",0,0,'h'}, {0,0,0,0} }; int main(int argc, char *argv[]) { int c; int permanent=0; progname = basename(argv[0]); if (vu_check() < 0) { fprintf(stderr,"This is a VUOS command." "It works only inside a vuos virtual namespace\n"); usage(); } while (1) { c=getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 'p': permanent=1; break; case 'h': usage(); break; } } if (argc - optind < 1) usage(); else { int rv=0; int i; for (i = optind; i < argc; i++) { if (vu_insmod(argv[i], permanent) < 0) { perror(argv[i]); rv=1; } } return rv; } return 0; } vuos-0.9.2/cmd/vu_lsmod.c000066400000000000000000000034571476575172100153050ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include static char *progname; void usage() { fprintf(stderr, "Usage:\n" " %s OPTIONS\n" " OPTIONS:\n" " -h --help: print this help message\n\n", progname); exit(2); } static const char *short_options = "p"; static const struct option long_options[] = { {"help",0,0,'h'}, {0,0,0,0} }; int main(int argc, char *argv[]) { int c; progname = basename(argv[0]); size_t bufsize; if (vu_check() < 0) { fprintf(stderr,"This is a VUOS command." "It works only inside a vuos virtual namespace\n"); usage(); } while (1) { c=getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 'h': usage(); break; } } if (argc - optind > 0) usage(); if ((bufsize = vu_lsmod(NULL, 0)) > 0) { char buf[bufsize]; if (vu_lsmod(buf, bufsize) < 0) perror(progname); else printf("%s", buf); } return 0; } vuos-0.9.2/cmd/vu_rmmod.c000066400000000000000000000034731476575172100153030ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include static char *progname; void usage() { fprintf(stderr, "Usage:\n" " %s OPTIONS vu_module [vu_module] ...\n" " OPTIONS:\n" " -h --help: print this help message\n\n", progname); exit(2); } static const char *short_options = "p"; static const struct option long_options[] = { {"help",0,0,'h'}, {0,0,0,0} }; int main(int argc, char *argv[]) { int c; progname = basename(argv[0]); if (vu_check() < 0) { fprintf(stderr,"This is a VUOS command." "It works only inside a vuos virtual namespace\n"); usage(); } while (1) { c=getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 'h': usage(); break; } } if (argc - optind < 1) usage(); else { int rv=0; int i; for (i = optind; i < argc; i++) { if (vu_rmmod(argv[i]) < 0) { perror(argv[i]); rv=1; } } return rv; } return 0; } vuos-0.9.2/cmd/vudebug.c000066400000000000000000000073451476575172100151160ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #define ALLTAGS "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" static char *progname; void usage() { fprintf(stderr, "Usage: %s [ARG] [ARG] ... [ -- cmd args ]\n" " ARG = --help | DEBUGSPEC\n" " DEBUGSPEC = [+|-]TAG[TAG]...[:COLORSPEC]\n" " +: add, -: delete ?:ls\n" " TAG one of: " ALLTAGS "\n" " COLORSPEC: nwrgbcmyNWRGBCMY+-_*#\n" " nwrgbcmy = black white red green blue cyan magenta yellow\n" " smallcase: foreground, capital: background\n" " +:,bright, -: dim, _: underlined, *: blinking, #: reverse\n\n", progname); exit(2); } static inline void unique(int c) { if (c != 0) usage(); } static void vu_ls_debugtags(char *lstags) { char tags[DEBUG_NTAGS+1]; char ltags[DEBUG_NTAGS+1]; int c; vu_get_debugtags(tags, DEBUG_NTAGS+1, 0); vu_get_debugtags(ltags, DEBUG_NTAGS+1, 1); for (c = ' '; c < 128; c++) { int tagselected = !!strchr(tags, c); int ltagselected = !!strchr(ltags, c); char tagname[32]; vu_get_debugtagname(c, tagname, 32); if (((lstags != NULL && strchr(lstags, c)) || (lstags == NULL && (tagselected || *tagname != '\0')))) printf("%c %c%s%s\n", c, tagselected ? '+' : '-', ltagselected ? "(+)" : " ", tagname); } } static char functions[] = "+-?"; int main(int argc, char *argv[]) { progname = basename(argv[0]); if (vu_check() < 0) { fprintf(stderr,"This is a VUOS command." "It works only inside a vuos virtual namespace\n"); usage(); } if (argc == 1) { char tags[DEBUG_NTAGS+1]; char ltags[DEBUG_NTAGS+1]; vu_get_debugtags(tags, DEBUG_NTAGS+1, 0); vu_get_debugtags(ltags, DEBUG_NTAGS+1, 1); if (*tags) printf("%s\n",tags); if (*ltags) printf("(%s)\n",ltags); } else if (argc == 2 && strcmp(argv[1], "--help") == 0) usage(); else { int i; char **cmdargv = NULL; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--") == 0) { argc = i; cmdargv = argv + i + 1; break; } } for (argc--, argv++; argc > 0; argc--, argv++) { char *arg = strdup(argv[0]); char *tags = arg; char function = 0; char *color; if (strchr(functions, *arg)) function = *tags++; if ((color = strchr(tags, ':')) != NULL) *color++ = 0; switch (function) { case '+': vu_add_debugtags(*tags ? tags : ALLTAGS, cmdargv != NULL); break; case '-': vu_del_debugtags(*tags ? tags : ALLTAGS, cmdargv != NULL); break; case '?': vu_ls_debugtags(*tags ? tags : NULL); break; default: if (color == NULL) usage(); break; } char *colorstring; if (asprintf(&colorstring, "%s:%s", *tags ? tags : ALLTAGS, (color) ? color : "") >= 0) { vu_set_debugcolor(colorstring); free(colorstring); } free(arg); } if (cmdargv != NULL) execvp(cmdargv[0], cmdargv); } return 0; } vuos-0.9.2/cmd/vumount.c000066400000000000000000000123141476575172100151620ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2019 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #define DEFAULT_FILESYSTEMTYPE "" #define DEFAULT_MOUNTFLAGS (MS_SILENT) static char default_options[] = "defaults"; struct mountopts_t { char *opt; unsigned long mountflags_on; unsigned long mountflags_off; }; struct mountopts_t mountopts[] = { {"defaults", 0, 0}, {"user", MS_NOSUID | MS_NODEV | MS_NOEXEC, 0}, {"group", MS_NOSUID | MS_NODEV, 0}, {"ro", MS_RDONLY, 0}, {"rw", 0, MS_RDONLY}, {"suid", 0, MS_NOSUID}, {"nosuid", MS_NOSUID, 0}, {"dev", 0, MS_NODEV}, {"nodev", MS_NODEV, 0}, {"exec", 0, MS_NOEXEC}, {"noexec", MS_NOEXEC, 0}, {"async", 0, MS_SYNCHRONOUS}, {"sync", MS_SYNCHRONOUS, 0}, {"remount", MS_REMOUNT, 0}, {"mand", MS_MANDLOCK, 0}, {"nomand", 0, MS_MANDLOCK}, {"dirsync", MS_DIRSYNC, 0}, {"atime", 0, MS_NOATIME}, {"noatime", MS_NOATIME, 0}, {"diratime", 0, MS_NODIRATIME}, {"nodiratime", MS_NODIRATIME, 0}, {"bind", MS_BIND, 0}, {"move", MS_MOVE, 0}, {"rbind", MS_BIND | MS_REC, 0}, {"silent", MS_SILENT, 0}, {"loud", 0, MS_SILENT}, {"acl", MS_POSIXACL, 0}, {"realtime", MS_RELATIME, 0}, {"norealtime", 0, MS_RELATIME}, {"iversion", MS_I_VERSION, 0}, {"noiversion", 0, MS_I_VERSION}, {"strictatime", MS_STRICTATIME, 0}, {"nostrictatime", 0, MS_STRICTATIME}, {"lazytime", MS_LAZYTIME, 0}, {"nolazytime", 0, MS_LAZYTIME}, }; #define NMO (int)(sizeof(mountopts) / sizeof(*mountopts)) int opt2flag(const char *mountopt, unsigned long *mountflags) { int i; for (i = 0; i < NMO; i++) { if (strcmp(mountopt, mountopts[i].opt) == 0) { *mountflags &= ~mountopts[i].mountflags_off; *mountflags |= mountopts[i].mountflags_on; return 1; } } return 0; } void usage(char *argv0) { char *name=basename(argv0); fprintf(stderr, "Usage:\n" " %s [options] \n\n" "Mount a filesystem.\n\n" "Options:\n" " -o, --options comma-separated list of mount options\n" " -t, --types limit the set of filesystem types\n" " -r, --read-only mount the filesystem read-only (same as -o ro)\n" " -w, --rw, --read-write mount the filesystem read-write (default)\n" " -B, --bind mount a subtree somewhere else (same as -o bind)\n" " -M, --move move a subtree to some other place\n" " -R, --rbind mount a subtree and all submounts somewhere else\n" " -h, --help display this help\n" "\n", name); exit(1); } char *parse_options(char *options, unsigned long *mountflags) { int tagc = stropt(options, NULL, NULL, 0); if(tagc > 0) { char *tags[tagc]; char *args[tagc]; stropt(options, tags, args, options); for (int i=0; i < tagc; i++) { if (args[i] == NULL && tags[i] != NULL && opt2flag(tags[i], mountflags) > 0) tags[i] = STROPTX_DELETED_TAG; } return stropt2str(tags, args, ',', '='); } else return options; } int main(int argc, char *argv[]) { char *progname = basename(argv[0]); char *source = NULL; char *target = NULL; char *filesystemtype = DEFAULT_FILESYSTEMTYPE; char *options = default_options; unsigned long mountflags = DEFAULT_MOUNTFLAGS; int c; static char *short_options = "ht:o:rwBMR"; static struct option long_options[] = { {"types", required_argument, 0, 't'}, {"options", required_argument, 0, 'o'}, {"read-only", no_argument, 0, 'r'}, {"read-write", no_argument, 0, 'w'}, {"rw", no_argument, 0, 'w'}, {"bind", no_argument, 0, 'B'}, {"move", no_argument, 0, 'M'}, {"rbind", no_argument, 0, 'R'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case 't': filesystemtype = optarg; break; case 'o': options = optarg; break; case 'r': opt2flag("ro", &mountflags); break; case 'w': opt2flag("rw", &mountflags); break; case 'B': opt2flag("bind", &mountflags); break; case 'M': opt2flag("move", &mountflags); break; case 'R': opt2flag("rbind", &mountflags); break; case 'h': default: usage(progname); break; } } if ((optind + 2) != argc) usage(progname); // this implies exit source = argv[optind]; target = argv[optind + 1]; options = parse_options(options, &mountflags); if (mount(source, target, filesystemtype, mountflags, options) < 0) perror(progname); return 0; } vuos-0.9.2/cmd/vuname.c000066400000000000000000000132021476575172100147350ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include char *progname; int quiet; struct vu_info vi; void version_exit(void) { printf("umviewname (VUOS project) 1.0\n" "Copyright (C) 2017 Virtualsquare Team\n" "This is free software. You may redistribute copies of it under the terms of\n" "the GNU General Public License .\n" "There is NO WARRANTY, to the extent permitted by law.\n" "Written by Renzo Davoli\n"); exit(0); } void usage_exit(int exit_status) { if (!quiet) { fprintf(stderr, "Usage: vuname [OPTION]...\n" " Print certain VUOS system information. With no OPTION, same as -s.\n" "Usage: vuname newname\n" " Set the VUOS view name\n" "\n" " -a, --all print all information, in the following order,\n" " except omit -p and -i if unknown:\n" " -s, --kernel-name print the kernel name\n" " -n, --nodename print the network node hostname\n" " -r, --kernel-release print the kernel release\n" " -v, --kernel-version print the kernel version\n" " -m, --machine print the machine hardware name\n" " -p, --processor print the processor type or \"unknown\"\n" " -i, --hardware-platform print the hardware platform or \"unknown\"\n" " -o, --operating-system print the operating system\n" " -U, --serverid print the server id\n" " -V, --viewname print the view name\n" "other options\n" " -P, --prompt print a string for user prompts\n" " -q, --quiet quiet mode: silent on errors\n" " -x, --nouname do not use uname when outside VUOS\n" " --help display this help and exit\n" " --version output version information and exit\n" "\n"); } exit(exit_status); } static char short_options[] = "snrvmpioUVaxqPN"; static char field_options[] = "snrvmpioUV"; static char vuos_options[] = " vv"; static char unknown[] = "unknown"; static char vuos[] = "GNU/Linux/VUOS"; static char no_vuos[] = "GNU/Linux"; static char *fields[] = { /* s */ vi.uname.sysname, /* n */ vi.uname.nodename, /* r */ vi.uname.release, /* v */ vi.uname.version, /* m */ vi.uname.machine, /* p */ unknown, /* i */ unknown, /* o */ no_vuos, /* U */ vi.vu_serverid, /* V */ vi.vu_name }; static struct option long_options[] = { {"all", 0, 0, 'a'}, {"kernel-name", 0, 0, 's'}, {"nodename",0,0,'n'}, {"kernel-release",0,0,'r'}, {"kernel-version",0,0,'v'}, {"machine",0,0,'m'}, {"processor",0,0,'p'}, {"hardware-platform",0,0,'i'}, {"operating-system",0,0,'o'}, {"serverid",0,0,'U'}, {"viewid",0,0,'V'}, {"viewname",0,0,'N'}, {"quiet",0,0,'q'}, {"nouname",0,0,'x'}, {"prompt",0,0,'P'}, {"help",0,0,0x100}, {"version",0,0,0x101}, {0,0,0,0} }; static int ch2fieldindex(char c) { char *c_in_opt = strchr(field_options, c); return (c_in_opt == NULL) ? -1 : c_in_opt - field_options; } int main(int argc, char *argv[]) { int prompt = 0; int flags = 0; int kernel_uname = 1; int ret_value; progname = basename(argv[0]); while (1) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) break; switch (c) { case 'a': flags = -1; break; case 'q': quiet = 1; break; case 'P': prompt = 1; break; case 'x': kernel_uname = 0; break; case 0x100: usage_exit(0); break; case 0x101: version_exit(); break; case 'N' : c = 'V'; /* for backwards compatibility */ __attribute__ ((fallthrough)); default: ret_value = ch2fieldindex(c); if (ret_value >= 0) flags |= 1 << ret_value; else usage_exit(1); break; } } if (argc - optind != 0) { /* with one argument and nooptions it sets the viewname */ if (argc - optind == 1 && optind == 1) { if (vu_setname(argv[optind]) == 0) exit(0); else { if (!quiet) perror(progname); exit (1); } } usage_exit(1); } ret_value = vu_getinfo(&vi); if (ret_value < 0) { if (kernel_uname) ret_value = uname(&vi.uname); if (ret_value < 0) { if (!quiet) perror(progname); exit (1); } } else { fields[ch2fieldindex('o')] = vuos; kernel_uname = 0; } if (prompt) { if (kernel_uname) printf("%s\n",vi.uname.nodename); else if (strlen(vi.vu_name) > 0) printf("%s\n",vi.vu_name); else printf("%s[%s]\n",vi.uname.nodename,vi.vu_serverid); } else if (flags == 0) printf("%s\n",vi.uname.sysname); else { unsigned int n; char *sep; for (n = 0, sep = ""; n < sizeof(field_options) - 1; n++) { if (flags & (1 << n) && (kernel_uname == 0 || vuos_options[n] == ' ') && (flags != -1 || fields[n] != unknown)) { printf("%s%s", sep, fields[n]); sep = " "; } } printf("\n"); } return 0; } vuos-0.9.2/cmd/vustack.c000066400000000000000000000164521476575172100151340ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include static char *progname; /* This is the command that creates the list of proocol family names echo "#include" | gcc -E -dD - | egrep '^#define *PF_.*[0-9]$' | \ awk 'BEGIN {printf "static char *pf_names[] = {\n"} {printf "\t[%s] = \"%s\",\n", $3, substr(tolower($2),4)} END {printf "};\n"}' */ static char *pf_names[] = { [0] = "unspec", [1] = "local", [2] = "inet", [3] = "ax25", [4] = "ipx", [5] = "appletalk", [6] = "netrom", [7] = "bridge", [8] = "atmpvc", [9] = "x25", [10] = "inet6", [11] = "rose", [12] = "decnet", [13] = "netbeui", [14] = "security", [15] = "key", [16] = "netlink", [17] = "packet", [18] = "ash", [19] = "econet", [20] = "atmsvc", [21] = "rds", [22] = "sna", [23] = "irda", [24] = "pppox", [25] = "wanpipe", [26] = "llc", [27] = "ib", [28] = "mpls", [29] = "can", [30] = "tipc", [31] = "bluetooth", [32] = "iucv", [33] = "rxrpc", [34] = "isdn", [35] = "phonet", [36] = "ieee802154", [37] = "caif", [38] = "alg", [39] = "nfc", [40] = "vsock", [41] = "kcm", [42] = "qipcrtr", [43] = "smc", [44] = "xdp", [45] = "mctp", [46] = "max", }; #define PF_NAMES_SIZE ((int)(sizeof(pf_names) / sizeof(*pf_names))) #define PF_EXTRA_SIZE (PF_NAMES_SIZE + 10) static unsigned char vustack_proto[PF_EXTRA_SIZE] = { [PF_UNSPEC] = 1 }; static char *pf_num2nname(int family) { if (family >= 0 && family < PF_NAMES_SIZE - 1) return pf_names[family]; else return "unknown"; } static int pf_name2num(char *pf_name) { int family; for (family = 0; family < PF_NAMES_SIZE; family++) if (pf_names[family] != NULL && strcmp(pf_name, pf_names[family]) == 0) return family; return -1; } static int is_a_stack(char *stack) { struct stat buf; int rv = stat(stack, &buf); if (rv < 0) return rv; else if ((buf.st_mode & S_IFMT) != S_IFSTACK) return errno=ENOTSUP, -1; else return 0; } static void add_supported_families(char *stack) { int family; vustack_proto[PF_UNSPEC] = 0; for (family = 1; family < PF_EXTRA_SIZE; family++) { int fd = msocket(stack, family, -1, 0); if (fd >= 0 || errno == EINVAL) vustack_proto[family] = 1; if (fd >= 0) close(fd); } } static void process_families(const char *input) { int tagc = stropt(input, NULL, NULL, 0); if(tagc > 0) { char buf[strlen(input)+1]; char *tags[tagc]; stropt(input, tags, NULL, buf); vustack_proto[PF_UNSPEC] = 0; for (int i=0; i < tagc - 1; i++) { switch(strcase_tolower(tags[i])) { case STRCASE(i,p): vustack_proto[PF_INET] = 1; vustack_proto[PF_INET6] = 1; vustack_proto[PF_NETLINK] = 1; vustack_proto[PF_PACKET] = 1; break; case STRCASE(i,p,v,4): case STRCASE(i,p,4): vustack_proto[PF_INET] = 1; break; case STRCASE(i,p,v,6): case STRCASE(i,p,6): vustack_proto[PF_INET6] = 1; break; case STRCASE(b,t): vustack_proto[PF_BLUETOOTH] = 1; break; case STRCASE(i,r): vustack_proto[PF_IRDA] = 1; break; default: if (isdigit(tags[i][0])) { int family = strtol(tags[i], NULL, 0); if (family > 0 && family < PF_EXTRA_SIZE) vustack_proto[family] = 1; else { fprintf(stderr, "%s: unknown protocol family %s\n", progname, tags[i]); exit(1); } } else { int family = pf_name2num(tags[i]); if (family > 0) vustack_proto[family] = 1; else { fprintf(stderr, "%s: unknown protocol family %s\n", progname, tags[i]); exit(1); } } } } } } static char *short_options = "hvsf:"; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"supported", no_argument, 0, 's'}, {"family", required_argument, 0, 'f'}, {"families", required_argument, 0, 'f'}, {0, 0, 0, 0} }; void usage(char *progname, int verbose) { fprintf(stderr, "%s: set the default networking stack\n\n" "Usage: %s [options] stack cmd [args]\n\n" " -h --help print this short usage message\n" " -f list\n" " -family list\n" " -families list set the list of address families\n" " -s --supported all families supported by the stack\n\n" " -v --verbose verbose mode\n\n", progname, progname); if (verbose) { int family; fprintf(stderr, "List address family names and numbers:\n"); for (family = 1; family < PF_NAMES_SIZE; family++) if (pf_names[family] != NULL) fprintf(stderr, "%5d: %s\n", family, pf_names[family]); fprintf(stderr, "\nMax family number %d\n", PF_EXTRA_SIZE - 1); fprintf(stderr, "\nAliases:\n" " ip: inet,inet6,netlink,packet\n" " ipv4: inet, ip4: inet, ipv6: inet6, ip6: inet6, bt: bluetooth, ir: irda\n\n"); } exit(1); } int main(int argc, char *argv[]) { int c; int verbose = 0; int supported = 0; progname = basename(argv[0]); if (vu_getinfo(NULL) < 0) { fprintf(stderr, "%s is a vuos command\n", progname); return 1; } while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case 'f': process_families(optarg); break; case 'v': verbose = 1; break; case 's': supported = 1; break; case 'h': default: usage(argv[0], verbose); break; } } if (argc > optind + 1) { char *stack = argv[optind]; char *cmd = argv[optind + 1]; char **newargv = argv + (optind + 1); if (is_a_stack(stack) < 0) { perror(stack); exit(1); } if (supported) add_supported_families(stack); if (vustack_proto[PF_UNSPEC] == 1) { if (verbose) fprintf(stderr, "Using %s for ALL address families\n", stack); if (msocket(stack, 0, SOCK_DEFAULT, 0) < 0) { perror("vustack: msocket"); exit(1); } } else { int family; if (verbose) { fprintf(stderr, "Using %s for the following address families:\n ", stack); for(family = 1; family < PF_EXTRA_SIZE; family++) { if (vustack_proto[family] == 1) { fprintf(stderr," %s(%d)", pf_num2nname(family), family); } } fprintf(stderr,"\n"); } for(family = 1; family < PF_EXTRA_SIZE; family++) { if (vustack_proto[family] == 1) { if (msocket(stack, family, SOCK_DEFAULT, 0) < 0) { perror("vustack: msocket"); exit(1); } } } } execvp(cmd, newargv); } else usage(progname, verbose); return 1; } vuos-0.9.2/cmd/vusu.c000066400000000000000000000126241476575172100144530ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include static char *short_options = "c:ls:mph"; static struct option long_options[] = { {"command", 1, 0, 'c'}, {"login", 0, 0, 'l'}, {"shell", 1, 0, 's'}, {"preserve-environment", 0, 0, 'p'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; char *command; int login; char *shell; int change_environment = 1; char *user; int uid; int gid; char *arg0; int ngroups; gid_t *groups; struct inheritenv { const char *tag; char *value; } inheritenv[] = { {.tag = "TERM"}, {.tag = "COLORTERM"}, {.tag = "DISPLAY"}, {.tag = "XAUTHORITY"}, {NULL, NULL} }; void usage(char *argv0) { char *name=basename(argv0); fprintf(stderr, "Usage: %s [options] [LOGIN]\n" "\n" "Options:\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n" " -h, --help display this help message and exit\n" " -, -l, --login make the shell a login shell\n" " -m, -p,\n" " --preserve-environment do not reset environment variables, and\n" " keep the same shell\n" " -s, --shell SHELL use SHELL instead of the default in passwd\n" "\n", name ); exit(2); } char *getlogindefs(char *tag) { char *line = NULL; size_t linelen = 0; FILE *f=fopen("/etc/login.defs","r"); char *retvalue = NULL; if (f) { while (retvalue == NULL && getline(&line, &linelen, f) > 0) { char *s = line; while (*s==' ' || *s=='\t') s++; if (*s=='#') continue; if (strncmp(tag,s,strlen(tag))!=0) continue; s+=strlen(tag); if (*s != ' ' && *s != '\t') continue; while (*s==' ' || *s=='\t') s++; s[strlen(s)-1]=0; retvalue = strdup(s); } fclose(f); if (line) free(line); } return retvalue; } void setpath(void) { char *tag = (uid==0)?"ENV_SUPATH":"ENV_PATH"; char *path = getlogindefs(tag); if (path) { char *cleanpath = path; if (strncmp("PATH=",cleanpath,5) == 0) cleanpath += 5; setenv("PATH",cleanpath,1); free(path); } else { if (uid) path="/bin:/usr/bin"; else path="/sbin:/bin:/usr/sbin:/usr/bin"; setenv("PATH",path,1); } } void loginenv(void) { int i; for (i = 0; inheritenv[i].tag != NULL; i++) { char *value = getenv(inheritenv[i].tag); if (value) inheritenv[i].value = strdup(value); } clearenv(); for (i = 0; inheritenv[i].tag != NULL; i++) { if (inheritenv[i].value) { setenv(inheritenv[i].tag, inheritenv[i].value, 1); free(inheritenv[i].value); } } } int main(int argc, char *argv[]) { int c; struct passwd *pwd; if (vu_getinfo(NULL) < 0) execvp("su",argv); while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case 'c': command=optarg; break; case 'l': login=1; break; case 's': shell=optarg; break; case 'm': case 'p': change_environment = 0; break; case 'h': usage(argv[0]); break; default: usage(argv[0]); break; } } if (argc > optind && strcmp(argv[optind],"-")==0) { login = 1; optind++; } if (argc > optind) user=argv[optind]; if (argc > optind+1) usage(argv[0]); if (user == NULL) user = "root"; pwd=getpwnam(user); if (pwd == NULL) { fprintf(stderr,"Unknown id: %s\n",user); exit(1); } uid=pwd->pw_uid; gid=pwd->pw_gid; if (login && change_environment) loginenv(); if (shell == NULL) { if (change_environment) shell = pwd->pw_shell; else shell = getenv("SHELL"); } if (shell == NULL) shell="/bin/sh"; unsetenv("IFS"); if (change_environment) { setenv("USER",user,1); setenv("LOGNAME",user,1); setenv("HOME",pwd->pw_dir,1); setenv("SHELL",shell,1); } if (login == 0 || asprintf(&arg0,"-%s",shell) < 0) arg0=shell; getgrouplist(user,gid,NULL,&ngroups); groups=malloc(ngroups * sizeof (gid_t)); if (groups == NULL) ngroups=0; else getgrouplist(user,gid,groups,&ngroups); if (setresuid(uid,uid,uid) < 0 || setresgid(gid,gid,gid) < 0) perror(argv[0]); else { setgroups(ngroups,groups); setpath(); if (login) { int defhome = 0; char *defhomeopt = getlogindefs("DEFAULT_HOME"); if (defhomeopt) { if (strcmp(defhomeopt, "yes") == 0) defhome = 1; free(defhomeopt); } if (chdir(pwd->pw_dir) < 0) { if (defhome == 0 || chdir("/") < 0) { perror(argv[0]); exit(1); } } } if (command) execl(shell,arg0,"-c",command,(char *)0); else execl(shell,arg0,(char *)0); perror(arg0); } exit(1); return 0; } vuos-0.9.2/cmd/vuumount.c000066400000000000000000000041361476575172100153520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2019 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include void usage(char *argv0) { char *name=basename(argv0); fprintf(stderr, "Usage:\n" " %s [options] \n\n" "Unmount a filesystem.\n\n" "Options:\n" " -f, --force force unmount (in case of an unreachable NFS system)\n" " -d, --detach-loop if mounted loop device, also free this loop device\n" " -h, --help display this help\n" "\n", name); exit(1); } int main(int argc, char *argv[]) { char *progname = basename(argv[0]); char *target = NULL; int flags = 0; int c; static char *short_options = "hfd"; static struct option long_options[] = { {"force", no_argument, 0, 'f'}, {"detach-loop", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case 'f': flags |= MNT_FORCE; break; case 'd': flags |= MNT_DETACH; break; case 'h': default: usage(progname); break; } } if ((optind + 1) != argc) usage(progname); // this implies exit target = argv[optind]; if (umount2(target, flags) < 0) perror(progname); return 0; } vuos-0.9.2/include/000077500000000000000000000000001476575172100141605ustar00rootroot00000000000000vuos-0.9.2/include/CMakeLists.txt000066400000000000000000000002351476575172100167200ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) file(GLOB VU_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) install(FILES ${VU_HEADERS} DESTINATION ${HEADERS_INSTALL_PATH}) vuos-0.9.2/include/config.h.in000066400000000000000000000003311476575172100162000ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H #define VUOS_VERSION "@CMAKE_PROJECT_VERSION@" #define MODULES_INSTALL_PATH "@MODULES_INSTALL_PATH@" #define VURC "vurc" #define ETC_VURC "@CONFIGURATION_DIR_PATH@/" VURC #endif vuos-0.9.2/include/libvumod.h000066400000000000000000000044201476575172100161520ustar00rootroot00000000000000#ifndef VUMODLIB_H #define VUMODLIB_H #include #include #include #include #include #include #include #include /* pseudo file mgmt */ #define PSEUDOFILE_LOAD_CONTENTS 1 #define PSEUDOFILE_STORE_CLOSE 2 #define PSEUDOFILE_LOAD_DIRENTS 3 /* upcall: this is the prototype upcall function registered by pseudofile_open, tag == PSEUDOFILE_LOAD_CONTENTS: upload the contents f-writing the file f (this happens at first read/write/lseek) tag == PSEUDOFILE_STORE_CLOSE: store/use the contents (and free any dynamic memory allocated for pseudoprivate). f can be NULL if the file has been never read or written. tag == PSEUDOFILE_LOAD_DIRENTS: populate the dir for getdents using pseudofile_filldir. (this appens at first getdents64) */ typedef int (* pseudo_upcall)(int tag, FILE *f, int openflags, void *pseudoprivate); /* helper function: convert struct stat's st_mode to struct dirent's d_type */ int pseudofile_mode2type(mode_t mode); /* helper function: use path to fill in the buf of bufsiz bytes for readlink. it returns -1/EINVAL if path is NULL */ ssize_t pseudofile_readlink_fill(char *path, char *buf, size_t bufsiz); /* add an entry to the dir for getdents */ int pseudofile_filldir(FILE *f, char *name, ino_t ino, char type); /* open a pseudofile: pseudoprivate is an opaque arg forwarded to the upcall. *private value must be stored and passed to all the other function here below. (e.g. using the private arg of the module's open syscall implementation */ int pseudofile_open(pseudo_upcall upcall, void *pseudoprivate, int flags, void **private); /* syscall implementation for pseudofiles. The signature of these function has been designed to be a drop in replacement for your module's functions. e.g. in yourmodule_init: struct vu_service_t *s = vu_mod_getservice(); vu_syscall_handler(s, close) = pseudofile_close; */ int pseudofile_close(int fd, void *private); int pseudofile_read(int fd, void *buf, size_t count, void *private); int pseudofile_write(int fd, const void *buf, size_t count, void *private); int pseudofile_lseek(int fd, off_t offset, int whence, void *private); int pseudofile_getdents64(int fd, struct dirent64 *dirp, unsigned int count, void *private); #endif vuos-0.9.2/include/vudev.h000066400000000000000000000045651476575172100154740ustar00rootroot00000000000000#ifndef VUDEV_H #define VUDEV_H #include /* header file for vudev submodules */ /* A vudev submodule must define a global non-static variable: struct vudev_operations_t vudev_ops = { .... } */ struct vudev_t; struct vudevfd_t { dev_t subdev; off_t offset; /* unused always 0 */ int flags; void *fdprivate; struct vudev_t *vudev; }; /* get the private data of the driver (return value of "init") */ void *vudev_get_private_data(struct vudev_t *vudev); /* set the device type (S_IFBLK or S_IFCHR) S_IFCHR is the default value */ void vudev_set_devtype(struct vudev_t *vudev, mode_t devtype); struct vudev_operations_t { /* confirm_function for devices with trailing numbers (e.g. hda1, hda2 etc) return 1 if this submodule manages that subdevice */ int (*confirm_subdev) (int subdev, struct vudev_t *vudev); int (*open) (const char *pathname, mode_t mode, struct vudevfd_t *vudevfd); int (*close) (int fd, struct vudevfd_t *vudevfd); /* when pread/pwrite are defined, read/write can be omitted. umdev translates read/write/seek and keeps track ofthe file position */ /* pread/pwrite are for block devices (seekable) read/write for char devices */ ssize_t (*read) (int fd, void *buf, size_t count, struct vudevfd_t *vudevfd); ssize_t (*write) (int fd, const void *buf, size_t count, struct vudevfd_t *vudevfd); ssize_t (*pread) (int fd, void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd); ssize_t (*pwrite) (int fd, const void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd); off_t (*lseek) (int fd, off_t offset, int whence, struct vudevfd_t *vudevfd); /* ioctl: * when fd == -1: return -1 if request already encodes dir and size (_IO/_IOR/_IOW/_IORX in ioctl.h. * otherwise return a fake request with the right dir and size * when fd >= 0: run the ioctl */ int (*ioctl) (int fd, unsigned long request, void *addr, struct vudevfd_t *vudevfd); /* management of poll/select/blocking requests */ int (*epoll_ctl) (int epfd, int op, int fd, struct epoll_event *event, struct vudevfd_t *vudevfd); /* constructor/destructor of the driver. * the return value of init: * - can be retrieved by vudev_get_private_data() * - is the private_data argument of fini */ void * (*init) (const char *source, unsigned long flags, const char *args, struct vudev_t *vudev); int (*fini) (void *private_data); }; #endif vuos-0.9.2/include/vulib.h000066400000000000000000000061121476575172100154520ustar00rootroot00000000000000#ifndef _VULIB_H #define _VULIB_H #include #include /* header file of libvulib. This library is for programs running in VUOS virtual machines. it provides access to the syscalls added by VUOS, if defines constants and structs for the syscalls */ /* Virtual System call numbers */ #define __VVU_insmod -1 #define __VVU_rmmod -2 #define __VVU_lsmod -3 #define __VVU_vuctl -4 #define __VVU_msocket -5 #define __NR_vu_insmod __VVU_insmod #define __NR_vu_rmmod __VVU_rmmod #define __NR_vu_lsmod __VVU_lsmod #define __NR_vuctl __VVU_vuctl #define __NR_msocket __VVU_msocket /* constants for vuctl */ #define VUCTL_GETINFO 1 #define VUCTL_SETNAME 2 #define VUCTL_GET_DEBUGTAGS 3 #define VUCTL_ADD_DEBUGTAGS 4 #define VUCTL_DEL_DEBUGTAGS 5 #define VUCTL_GET_DEBUGTAGNAME 6 #define VUCTL_SET_DEBUGCOLOR 7 #if 0 /* not yet implemented */ #define VUCTL_ATTACH #endif /* in VUOS network stacks can be "mounted" in the file system. S_IFSTACK is the file type of a network stack */ #ifndef S_IFSTACK #define S_IFSTACK 0160000 #endif /* 0 is not a valid socket type, it is used by msocket to define the default stack for a give family: msocket("/dev/net/mystack", AF_INET, SOCK_DEFAULT, 0); sets the default stack for ipv4 socket to "/dev/net/mystack" */ #ifndef SOCK_DEFAULT #define SOCK_DEFAULT 0 #endif /* debug tags for vuctl VUCTL_*_DEBUGTAGS */ #define DEBUG_ALLTAGS " ABCDEFGHIJKLMNOPQRSTUVWXYZ_01234abcdefghijklmnopqrstuvwxyz56789" #define DEBUG_NTAGS sizeof(DEBUG_ALLTAGS) /* struct for vuctl VUCTL_GETINFO */ struct vu_info { struct utsname uname; char vu_serverid[_UTSNAME_LENGTH]; char vu_name[_UTSNAME_LENGTH]; }; #ifndef _VU_HYPERVISOR static inline long vu_insmod(char *module, int permanent) { return syscall(__NR_vu_insmod, module, permanent); } static inline long vu_rmmod(char *modname) { return syscall(__NR_vu_rmmod, modname); } static inline long vu_lsmod(char *buf, size_t len) { return syscall(__NR_vu_lsmod, buf, len); } static inline long vu_getinfo(struct vu_info *info) { return syscall(__NR_vuctl, VUCTL_GETINFO, info); } static inline long vu_check(void) { return syscall(__NR_vuctl, VUCTL_GETINFO, NULL); } static inline long vu_setname(char *vuname) { return syscall(__NR_vuctl, VUCTL_SETNAME, vuname); } static inline long vu_get_debugtags(char *debugtags, size_t len, int local) { return syscall(__NR_vuctl, VUCTL_GET_DEBUGTAGS, debugtags, len, local); } static inline long vu_add_debugtags(char *debugtags, int local) { return syscall(__NR_vuctl, VUCTL_ADD_DEBUGTAGS, debugtags, local); } static inline long vu_del_debugtags(char *debugtags, int local) { return syscall(__NR_vuctl, VUCTL_DEL_DEBUGTAGS, debugtags, local); } static inline long vu_get_debugtagname(int tag, char *string, size_t len) { return syscall(__NR_vuctl, VUCTL_GET_DEBUGTAGNAME, tag, string, len); } static inline long vu_set_debugcolor(char *debugcolor) { return syscall(__NR_vuctl, VUCTL_SET_DEBUGCOLOR, debugcolor); } static inline long msocket(char *stack, int domain, int type, int protocol) { return syscall(__NR_msocket, stack, domain, type, protocol); } #endif #endif vuos-0.9.2/include/vumisc.h000066400000000000000000000016721476575172100156450ustar00rootroot00000000000000#ifndef VUMISC_H #define VUMISC_H #include #include struct vumisc_info { char *path; struct vu_stat stat; void *upcall_private; }; /* get the private data (return value of "init") */ void *vumisc_get_private_data(void); struct vumisc_operations_t { struct vumisc_info *infotree; pseudo_upcall infocontents; /* constructor/destructor of the submodule. * the return value of init: * - can be retrieved by vudev_get_private_data() * - is the private_data argument of fini */ void * (*init) (const char *source); int (*fini) (void *private_data); }; #define VUMISC_SYSNAME(name, syscall) name ## _ ## syscall #define VUMISC_PROTOTYPES(name) \ int VUMISC_SYSNAME(name, clock_getres) (clockid_t clk_id, struct timespec *res); \ int VUMISC_SYSNAME(name, clock_gettime) (clockid_t clk_id, struct timespec *tp); \ int VUMISC_SYSNAME(name, clock_settime) (clockid_t clk_id, const struct timespec *tp); \ #endif vuos-0.9.2/include/vumodule.h000066400000000000000000000337311476575172100162000ustar00rootroot00000000000000#ifndef VUMODULE_H #define VUMODULE_H #include #include #include #include #include #include #include #include #include #include #include #include #include /* header file for VUOS module implementation */ /* a module must define a struct vu_module_t global non static variable named vu_module*/ /* if the name of the module (vu_module.name) is xxx: * the module constructor is: * void *vu_xxx_init(void) * the destructor is: * int vu_xxx_fini(void *private_data); * system call handlers can be defined in two ways: * by defining a function whose name has the prefix vu_xxx_ (like vu_xxx_open, vu_xxx_write) * using the macro vu_syscall_handler in the constructor * (the former way is used to define an implementation of the system call, the latter way * is preferred to use an existing function (e.g. the system call implementation by glibc) */ /* when a module is loaded, it defines a service. A service can be seen as a descriptor for a module. */ struct vu_service_t; struct vuht_entry_t; struct vu_module_t { char *name; char *description; uint64_t flags; uint64_t filler; }; /* flags: * + VU_USE_PRW: use pread/pwrite instead of read/write. vuos core keeps track of the file * current offset and current size. * The module read/write methods are not used (can be omitted except for char devices) * The 'seek' method can be safely omitted. When defined it confirms the consistency of the * file position (e.g. for seekable devices max size) * * off_t modulename_lseek(int fd, off_t offset, int whence, void *fdprivate) { * ... * switch (whence) { * case SEEK_SET: if (offset <= device_size) * return offset; * else * return errno = EINVAL, -1; * case SEEK_END: return device_size; * default: return errno = EINVAL, -1; * } * } */ #define VU_USE_PRW (1L << 0) typedef unsigned long int syscall_arg_t; typedef long (*syscall_t)(); extern const uint16_t vu_arch_table[]; extern char *mountflag_strings[32]; syscall_t *vu_syscall_handler_pointer(struct vu_service_t *service, char *name); #define vu_syscall_handler(s, n) (*(vu_syscall_handler_pointer(s, #n))) /* stat must be implemented using 64bit data structures even if VUOS is running on a 32bit architecture. always use vu_stat/vu_lstat when writing module code */ #if __WORDSIZE == 32 #define __VU_vu_lstat __VU_lstat64 #define vu_stat stat64 #define vu_lstat lstat64 #else #define vu_stat stat #define vu_lstat lstat #endif /* the macro VU_PROTOTYPES gets the name of the module as its argument. VU_PROTOTYPES defines the signature of all possible module define functions that VUOS core can call. VU_PROTOTYPES is for type checking */ /* deallocation of data-structures must be in the cleanup function. umount should only delete the entry/entries from the hash table (vuht_del) */ #define VU_SYSNAME(name, syscall) vu_ ## name ## _ ## syscall #define VU_PROTOTYPES(name) \ \ void * VU_SYSNAME(name, init) (void); \ int VU_SYSNAME(name, fini) (void *); \ int VU_SYSNAME(name, lstat) (char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate); \ int VU_SYSNAME(name, access) (char *path, int mode, int flags); \ ssize_t VU_SYSNAME(name, readlink) (char *path, char *buf, size_t bufsiz); \ int VU_SYSNAME(name, open) (const char *pathname, int flags, mode_t mode, void **fdprivate); \ int VU_SYSNAME(name, close) (int fd, void *fdprivate); \ ssize_t VU_SYSNAME(name, read) (int fd, void *buf, size_t count, void *fdprivate); \ ssize_t VU_SYSNAME(name, write)(int fd, const void *buf, size_t count, void *fdprivate); \ ssize_t VU_SYSNAME(name, pread64) (int fd, void *buf, size_t count, off_t offset, int flags, void *fdprivate); \ ssize_t VU_SYSNAME(name, pwrite64)(int fd, const void *buf, size_t count, off_t offset, int flags, void *fdprivate); \ int VU_SYSNAME(name, getdents64) (unsigned int fd, struct dirent64 *dirp, unsigned int count, void *fdprivate); \ off_t VU_SYSNAME(name, lseek) (int fd, off_t offset, int whence, void *fdprivate); \ int VU_SYSNAME(name, ioctl) (int fd, unsigned long request, void *buf, uintptr_t addr, void *fdprivate); \ int VU_SYSNAME(name, unlink) (const char *pathname); \ int VU_SYSNAME(name, truncate) (const char *path, off_t length, int fd, void *fdprivate); \ int VU_SYSNAME(name, mkdir) (const char *pathname, mode_t mode); \ int VU_SYSNAME(name, mknod) (const char *pathname, mode_t mode, dev_t dev); \ int VU_SYSNAME(name, rmdir) (const char *pathname); \ int VU_SYSNAME(name, chmod) (const char *pathname, mode_t mode, int fd, void *fdprivate); \ int VU_SYSNAME(name, lchown) (const char *pathname, uid_t owner, gid_t group, int fd, void *fdprivate); \ int VU_SYSNAME(name, utimensat) (int dirfd, const char *pathname, \ const struct timespec times[2], int flags, int fd, void *fdprivate); \ int VU_SYSNAME(name, symlink) (const char *target, const char *linkpath); \ int VU_SYSNAME(name, link) (const char *target, const char *linkpath); \ int VU_SYSNAME(name, rename) (const char *target, const char *linkpath, int flags); \ int VU_SYSNAME(name, statfs) (const char *pathname, struct statfs *buf, int fd, void *fdprivate); \ int VU_SYSNAME(name, mount) (const char *source, const char *target, \ const char *filesystemtype, unsigned long mountflags, \ const void *data); \ int VU_SYSNAME(name, umount2) (const char *target, int flags); \ ssize_t VU_SYSNAME(name, lgetxattr) (const char *path, const char *name, \ void *value, size_t size, int fd, void *fdprivate); \ int VU_SYSNAME(name, lsetxattr) (const char *path, const char *name, \ const void *value, size_t size, int flags, int fd, void *fdprivate); \ ssize_t VU_SYSNAME(name, llistxattr) (const char *path, \ char *list, size_t size, int fd, void *fdprivate); \ int VU_SYSNAME(name, lremovexattr) (const char *path, const char *name, int fd, void *fdprivate); \ int VU_SYSNAME(name, epoll_ctl) (int epfd, int op, int fd, struct epoll_event *event, void *fdprivate); \ int VU_SYSNAME(name, getresfuid) (uid_t *ruid, uid_t *euid, uid_t *suid, uid_t *fsuid, void *private); \ int VU_SYSNAME(name, getresfgid) (gid_t *rgid, gid_t *egid, gid_t *sgid, gid_t *fsgid, void *private); \ int VU_SYSNAME(name, setresfuid) (uid_t ruid, uid_t euid, uid_t suid, uid_t fsuid, void *private); \ int VU_SYSNAME(name, setresfgid) (gid_t rgid, gid_t egid, gid_t sgid, gid_t fsgid, void *private); \ int VU_SYSNAME(name, socket) (int domain, int type, int protocol, void **fdprivate); \ int VU_SYSNAME(name, bind) (int sockfd, const struct sockaddr *addr, socklen_t addrlen, void *fdprivate); \ int VU_SYSNAME(name, connect) (int sockfd, const struct sockaddr *addr, socklen_t addrlen, void *fdprivate); \ int VU_SYSNAME(name, listen) (int sockfd, int backlog, void *fdprivate); \ int VU_SYSNAME(name, accept4) (int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, void *fdprivate); \ int VU_SYSNAME(name, getsockname) (int sockfd, struct sockaddr *addr, socklen_t *addrlen, void *fdprivate); \ int VU_SYSNAME(name, getpeername) (int sockfd, struct sockaddr *addr, socklen_t *addrlen, void *fdprivate); \ ssize_t VU_SYSNAME(name, sendto) (int sockfd, const void *buf, size_t len, int flags, \ const struct sockaddr *dest_addr, socklen_t addrlen, \ void *msg_control, size_t msg_controllen, void *fdprivate); \ ssize_t VU_SYSNAME(name, recvfrom) (int sockfd, void *buf, size_t len, int flags, \ struct sockaddr *src_addr, socklen_t *addrlen, \ void *msg_control, size_t *msg_controllen, void *fdprivate); \ int VU_SYSNAME(name, getsockopt) (int sockfd, int level, int optname, \ void *optval, socklen_t *optlen, void *fdprivate); \ int VU_SYSNAME(name, setsockopt) (int sockfd, int level, int optname, \ const void *optval, socklen_t optlen, void *fdprivate); \ int VU_SYSNAME(name, capget) (cap_user_header_t hdrp, cap_user_data_t datap); \ int VU_SYSNAME(name, capset) (cap_user_header_t hdrp, const cap_user_data_t datap); \ int VU_SYSNAME(name, clock_gettime) (clockid_t clk_id, struct timespec *tp); \ int VU_SYSNAME(name, clock_settime) (clockid_t clk_id, const struct timespec *tp); \ int VU_SYSNAME(name, clock_getres) (clockid_t clk_id, struct timespec *res); \ \ void VU_SYSNAME(name, cleanup) (uint8_t type, void *arg, int arglen, \ struct vuht_entry_t *ht); \ /* HASH TABLE management functions */ #define CHECKMODULE 0 // Module name #define CHECKPATH 1 // Path #define CHECKSOCKET 2 // Address Family #define CHECKCHRDEVICE 3 // chr device maj/min #define CHECKBLKDEVICE 4 // blk device #define CHECKSC 5 // Syscall # #define CHECKIOCTL 6 // ioctl request #define CHECKBINFMT 7 // Binfmt search #define SET_EPOCH 1 #define NEGATIVE_MOUNT ((confirmfun_t)1) #define VUFLAG_PERMANENT 1 #define VUFLAG_TRAILINGNUMBERS 2 typedef int (*confirmfun_t)(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht); struct vuht_entry_t *vuht_add(uint8_t type, const void *obj, int objlen, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data); struct vuht_entry_t *vuht_pathadd(uint8_t type, const char *source, const char *path, const char *fstype, unsigned long mountflags, const char *mountopts, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data); #define BINFMTBUFLEN 128 #define BINFMTLINELEN 1920 #define BINFMT_PRESERVE_ARGV0 0x1 #define BINFMT_OPEN_BINARY 0x2 #define BINFMT_CREDENTIALS 0x4 struct binfmt_req_t { char *path; char filehead[BINFMTBUFLEN + 2]; int fileheadlen; int flags; }; /* mainly for modules' threads */ struct vuht_entry_t *vu_mod_getht(void); void vu_mod_setht(struct vuht_entry_t *ht); pid_t vu_mod_gettid(); int vu_mod_getsyscall_number(void); syscall_arg_t vu_mod_getsyscall_arg(unsigned int narg); /* 0 - 5 */ void vu_mod_peek_str(void *addr, void *buf, size_t datalen); char *vu_mod_peekdup_path(void *addr); void vu_mod_peek_data(void *addr, void *buf, size_t datalen); void vu_mod_poke_data(void *addr, void *buf, size_t datalen); mode_t vu_mod_getumask(void); mode_t vu_mod_getmode(void); int vu_mod_fd_get_sfd(int fd, void **fdprivate); struct vuht_entry_t *vu_mod_fd_get_ht(int fd); struct vu_service_t *vuht_get_service(struct vuht_entry_t *hte); __attribute__((always_inline)) static inline struct vu_service_t * vu_mod_getservice(void) { return vuht_get_service(vu_mod_getht()); } const void *vuht_get_obj(struct vuht_entry_t *hte); int vuht_get_objlen(struct vuht_entry_t *hte); void *vuht_get_private_data(struct vuht_entry_t *hte); void vuht_set_private_data(struct vuht_entry_t *hte, void *ht_private_data); __attribute__((always_inline)) static inline void *vu_get_ht_private_data(void) { return vuht_get_private_data(vu_mod_getht()); } __attribute__((always_inline)) static inline void vu_set_ht_private_data(void *ht_private_data) { vuht_set_private_data(vu_mod_getht(), ht_private_data); } /* supported flags: MNT_FORCE MNT_DETACH (both provide immediate detach and lazy delete) */ int vuht_del(struct vuht_entry_t *hte, int umountflags); /* load submodules */ void *vu_mod_dlopen(const char *modname, int flags); /* inheritance management, define an upcall function to track process creation, exec, and termination */ /* MOD_INH_CLONE happens before process creation, MOD_INH_START is an event of the new tracing thread. The return value of clone is delivered as a parameter to start */ typedef enum mod_inheritance_state_t { MOD_INH_CLONE, MOD_INH_START, MOD_INH_EXEC, MOD_INH_TERMINATE } mod_inheritance_state_t; struct mod_inheritance_exec_arg { uid_t exec_uid; gid_t exec_gid; }; typedef void *(*mod_inheritance_upcall_t) (mod_inheritance_state_t state, void *ioarg, void *arg); void mod_inheritance_upcall_register(mod_inheritance_upcall_t upcall); void mod_inheritance_upcall_deregister(mod_inheritance_upcall_t upcall); /* log/debug facilities */ #define KERN_SOH "\001" /* ASCII Start Of Header */ #define KERN_EMERG KERN_SOH "0" /* system is unusable */ #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ #define KERN_CRIT KERN_SOH "2" /* critical conditions */ #define KERN_ERR KERN_SOH "3" /* error conditions */ #define KERN_WARNING KERN_SOH "4" /* warning conditions */ #define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */ #define KERN_INFO KERN_SOH "6" /* informational */ #define KERN_DEBUG KERN_SOH "7" /*debug-level messages */ void _debug_set_name(int index, const char *s); void debug_get_name(char tag, char *buf, size_t bufsize); #define debug_set_name(tag, s) \ _debug_set_name(DEBUG_TAG2INDEX_##tag, "" s) int vprintk(const char *fmt, va_list ap); int printk(const char *fmt, ...); extern uint64_t debugmask; extern __thread uint64_t tdebugmask; extern __thread pid_t debugtid; int _printkdebug(int index, const char *fmt, ...); #define printkdebug(tag, fmt, ...) \ if (__builtin_expect((debugmask | tdebugmask) & (1ULL << DEBUG_TAG2INDEX_##tag), 0)) \ _printkdebug(DEBUG_TAG2INDEX_##tag, "%d:%d\040%s:%d " fmt "\n", \ debugtid, gettid(), basename(__FILE__), __LINE__, ##__VA_ARGS__) #define DEBUG_TAG2INDEX_A 1 #define DEBUG_TAG2INDEX_B 2 #define DEBUG_TAG2INDEX_C 3 #define DEBUG_TAG2INDEX_D 4 #define DEBUG_TAG2INDEX_E 5 #define DEBUG_TAG2INDEX_F 6 #define DEBUG_TAG2INDEX_G 7 #define DEBUG_TAG2INDEX_H 8 #define DEBUG_TAG2INDEX_I 9 #define DEBUG_TAG2INDEX_J 10 #define DEBUG_TAG2INDEX_K 11 #define DEBUG_TAG2INDEX_L 12 #define DEBUG_TAG2INDEX_M 13 #define DEBUG_TAG2INDEX_N 14 #define DEBUG_TAG2INDEX_O 15 #define DEBUG_TAG2INDEX_P 16 #define DEBUG_TAG2INDEX_Q 17 #define DEBUG_TAG2INDEX_R 18 #define DEBUG_TAG2INDEX_S 19 #define DEBUG_TAG2INDEX_T 20 #define DEBUG_TAG2INDEX_U 21 #define DEBUG_TAG2INDEX_V 22 #define DEBUG_TAG2INDEX_W 23 #define DEBUG_TAG2INDEX_X 24 #define DEBUG_TAG2INDEX_Y 25 #define DEBUG_TAG2INDEX_Z 26 #endif vuos-0.9.2/include/vunet.h000066400000000000000000000053661476575172100155040ustar00rootroot00000000000000#ifndef _VUNET_H #define _VUNET_H #include #include #include #include #include #include /* header file for vunet submodules */ /* A vunet submodule must define a global non-static variable: struct vunet_operations_t vunet_ops = { .... } */ #ifndef S_IFSTACK #define S_IFSTACK 0160000 #endif #ifndef SOCK_DEFAULT #define SOCK_DEFAULT 0 #endif /* set and get private data of a fd */ void *vunet_get_fdprivate(void); void vunet_set_fdprivate(void *fdprivate); /* get the private_data of the stack (set by init below) */ void *vunet_get_private_data(void); struct vunet_operations { int (*socket) (int, int, int); int (*bind) (int, const struct sockaddr *, socklen_t); int (*connect) (int, const struct sockaddr *, socklen_t); int (*listen) (int, int); int (*accept4) (int, struct sockaddr *, socklen_t *, int flags); int (*getsockname) (int, struct sockaddr *, socklen_t *); int (*getpeername) (int, struct sockaddr *, socklen_t *); /* read, recv, recvfrom, recvmsg are all converted in recvmsg */ ssize_t (*recvmsg)(int, struct msghdr *, int); /* write, send, sendto, sendmsg are all converted in sendmsg */ ssize_t (*sendmsg)(int, const struct msghdr *, int); int (*setsockopt) (int, int, int, const void *, socklen_t); int (*getsockopt) (int, int, int, void *, socklen_t *); int (*shutdown) (int, int); /* ioctl: * when fd == -1: return -1 if request already encodes dir and size (_IO/_IOR/_IOW/_IORX in ioctl.h. * otherwise return a fake request with the right dir and size * when fd >= 0: run the ioctl */ int (*ioctl) (int, unsigned long, void *); int (*close) (int); int (*fcntl) (int, int, long); /* management of poll/select/blocking requests */ int (*epoll_ctl) (int epfd, int op, int fd, struct epoll_event *event); /* return 1 if the submodule supports the address family, 0 otherwise */ int (*supported_domain) (int domain); /* return 1 if the submodule supports the ioctl request, 0 otherwise */ int (*supported_ioctl) (unsigned long request); /* constructor/destructor of the stack. * *private_data in init: * - can be retrieved by vunet_get_private_data() * - is the private_data argument of fini */ int (*init) (const char *source, unsigned long flags, const char *args, void **private_data); int (*fini) (void *private_data); }; /* helper functions for ioctl: * vunet_is_netdev_ioctl returns a boolean: 1 (true) if the request is a netdevice ioctl (see netdevice(7)). * vunet_ioctl_parms convert network related requests in dir/size. * (netdevice ioctl, FIONREAD, FIONBIO use an old ancoding which do not encode dir/size. */ int vunet_is_netdev_ioctl(unsigned long request); long vunet_ioctl_parms(unsigned long request); #endif vuos-0.9.2/libvumod/000077500000000000000000000000001476575172100143565ustar00rootroot00000000000000vuos-0.9.2/libvumod/CMakeLists.txt000066400000000000000000000010161476575172100171140ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(VUMODLIB_VERSION_STRING 0.0.1) set(VUMODLIB_VERSION_MAJOR 0) include_directories(${VU_HEADERS}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VUMODLIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(vumod SHARED ${VUMODLIB_SOURCES}) target_link_libraries(vumod volatilestream) set_target_properties(vumod PROPERTIES VERSION ${VUMODLIB_VERSION_STRING} SOVERSION ${VUMODLIB_VERSION_MAJOR}) install(TARGETS vumod LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) vuos-0.9.2/libvumod/vu_mod_pseudofile.c000066400000000000000000000133371476575172100202410ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include typedef int (* pseudo_upcall)(int tag, FILE *f, int openflags, void *pseudoprivate); struct pseudofile { pseudo_upcall upcall; void *pseudoprivate; int flags; FILE *f; }; int pseudofile_mode2type(mode_t mode) { if (S_ISREG(mode)) return DT_REG; else if (S_ISDIR(mode)) return DT_DIR; else if (S_ISLNK(mode)) return DT_LNK; else if (S_ISCHR(mode)) return DT_CHR; else if (S_ISBLK(mode)) return DT_BLK; else if (S_ISSOCK(mode)) return DT_SOCK; else if (S_ISFIFO(mode)) return DT_FIFO; else return DT_UNKNOWN; } ssize_t pseudofile_readlink_fill(char *path, char *buf, size_t bufsiz) { if (path == NULL) { errno = EINVAL; return -1; } else { size_t len = strlen(path); if (len > bufsiz) len = bufsiz; mempcpy(buf, path, len); if (len < bufsiz) buf[len] = 0; return len; } } int pseudofile_filldir(FILE *f, char *name, ino_t ino, char type) { struct dirent64 entry = { .d_ino = ino, .d_type = type }; static char filler[7]; unsigned short int namelen = strlen(name) + 1; unsigned short int reclen = offsetof(struct dirent64, d_name) + namelen; int ret_value; snprintf(entry.d_name, 256, "%s", name); /* entries are always 8 bytes aligned */ entry.d_reclen = (reclen + 7) & (~7); ret_value = fwrite(&entry, reclen, 1, f); /* add a filler to align the next entry */ if (entry.d_reclen > reclen) ret_value += fwrite(filler, entry.d_reclen - reclen, 1, f); return ret_value; } int pseudofile_open(pseudo_upcall upcall, void *pseudoprivate, int flags, void **private) { struct pseudofile *pseudofile = malloc(sizeof(struct pseudofile)); pseudofile->upcall = upcall; pseudofile->pseudoprivate = pseudoprivate; pseudofile->flags = flags; pseudofile->f = NULL; *private = pseudofile; return 0; } static void pseudofile_load_contents(struct pseudofile *pseudofile) { pseudofile->f = volstream_open(); if ((pseudofile->flags & O_ACCMODE) != O_WRONLY && !(pseudofile->flags & O_TRUNC)) { pseudofile->upcall(PSEUDOFILE_LOAD_CONTENTS, pseudofile->f, pseudofile->flags, pseudofile->pseudoprivate); fflush(pseudofile->f); fseeko(pseudofile->f, 0, SEEK_SET); } } int pseudofile_close(int fd, void *private) { struct pseudofile *pseudofile = private; if (pseudofile) { if (pseudofile->f) fseeko(pseudofile->f, 0, SEEK_SET); pseudofile->upcall(PSEUDOFILE_STORE_CLOSE, pseudofile->f, pseudofile->flags, pseudofile->pseudoprivate); if (pseudofile->f) fclose(pseudofile->f); free(pseudofile); } return 0; } int pseudofile_read(int fd, void *buf, size_t count, void *private) { struct pseudofile *pseudofile = private; if (pseudofile == NULL) { errno = EINVAL; return -1; } if ((pseudofile->flags & O_ACCMODE) == O_WRONLY) { errno = EBADF; return -1; } if (pseudofile->f == NULL) pseudofile_load_contents(pseudofile); return fread(buf, 1, count, pseudofile->f); } int pseudofile_write(int fd, const void *buf, size_t count, void *private) { struct pseudofile *pseudofile = private; if (pseudofile == NULL) { errno = EINVAL; return -1; } if ((pseudofile->flags & O_ACCMODE) == O_RDONLY) { errno = EBADF; return -1; } if (pseudofile->f == NULL) pseudofile_load_contents(pseudofile); return fwrite(buf, 1, count, pseudofile->f); } int pseudofile_lseek(int fd, off_t offset, int whence, void *private) { struct pseudofile *pseudofile = private; if (pseudofile == NULL) { errno = EINVAL; return -1; } if (pseudofile->f == NULL) pseudofile_load_contents(pseudofile); return fseeko(pseudofile->f, offset, whence); } int pseudofile_getdents64(int fd, struct dirent64 *dirp, unsigned int count, void *private) { struct pseudofile *pseudofile = private; size_t freadout; if (pseudofile == NULL) { errno = EINVAL; return -1; } if (pseudofile->f == NULL) { pseudofile->f = volstream_open(); pseudofile->upcall(PSEUDOFILE_LOAD_DIRENTS, pseudofile->f, pseudofile->flags, pseudofile->pseudoprivate); fflush(pseudofile->f); fseeko(pseudofile->f, 0, SEEK_SET); } freadout = fread(dirp, 1, count, pseudofile->f); /* if the buffer is full the last entry might be incomplete. update freadout to drop the last incomplete entry, and seek back the position in the file to reread it from its beginning at the next getdents64 */ if (freadout == count) { unsigned int bpos = 0; struct dirent64 *d; char *buf = (char *) dirp; while (1) { d = (struct dirent64 *) (buf + bpos); if (count - bpos < offsetof(struct dirent64, d_name)) break; if (bpos + d->d_reclen > count) break; bpos += d->d_reclen; } if (bpos < count) { fseeko(pseudofile->f, - (int) (count - bpos), SEEK_CUR); freadout -= count - bpos; } /* the buffer is so short that it does not fit one entry. Return EINVAL! */ if (freadout == 0) { errno = EINVAL; return -1; } } return freadout; } vuos-0.9.2/man/000077500000000000000000000000001476575172100133105ustar00rootroot00000000000000vuos-0.9.2/man/CMakeLists.txt000066400000000000000000000017501476575172100160530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(PANDOC_ORG "VirtualSquare-VUOS") # ### pandoc pages file(GLOB VU_PANDOC_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8].md) set(VU_MAN_FILES) foreach(VU_PANDOC_PATH IN LISTS VU_PANDOC_PAGES) # VU_PANDOCPAGE: basename of VU_PANDOC_PATH get_filename_component(VU_PANDOCPAGE ${VU_PANDOC_PATH} NAME) # VU_MANPAGE: VU_PANDOCPAGE without the suffix string(REGEX REPLACE "\.md$" "" VU_MANPAGE ${VU_PANDOCPAGE}) list(APPEND VU_MAN_FILES ${VU_MANPAGE}) endforeach(VU_PANDOC_PATH) add_custom_target(${PROJECT_NAME}_manpages ALL make PANDOC_ORG="${PANDOC_ORG}" ${VU_MAN_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) ### man pages file(GLOB VU_MAN_PAGES ${CMAKE_CURRENT_SOURCE_DIR}/*.[1-8]) foreach(VU_MAN_PATH IN LISTS VU_MAN_PAGES) get_filename_component(VU_MANPAGE ${VU_MAN_PATH} NAME) string(REGEX REPLACE ".*\\." "" MAN_CHAPTER ${VU_MANPAGE}) install(FILES ${VU_MAN_PATH} DESTINATION ${CMAKE_INSTALL_MANDIR}/man${MAN_CHAPTER}) endforeach(VU_MAN_PATH) vuos-0.9.2/man/Makefile000066400000000000000000000022351476575172100147520ustar00rootroot00000000000000MAKEFLAGS += --silent PANDOC=pandoc PANDOCOK := $(shell command -v ${PANDOC} 2> /dev/null) PANDOCMINVER=3.1.7 ifdef PANDOCOK PANDOCVER := $(shell ${PANDOC} -v | head -1 | cut -d ' ' -f 2) PANDOCVEROK := $(shell printf '%s\n' ${PANDOCMINVER} ${PANDOCVER} | sort -C -V; echo $$?) endif none: % : %.md ifdef PANDOCOK ifeq (${PANDOCVEROK}, 1) echo "${PANDOC} ${PANDOCVER} < ${PANDOCMINVER}. $@ can create font warnings with man/groff" >&2 endif # copy copyright notice grep "^\.\\\\\"" $< > $@ || true # run pandoc $(eval SECTION := $(subst .,,$(suffix $@))) $(eval BASENAME := $(basename $@)) $(eval TITLE := $(shell echo "${BASENAME}\(${SECTION}\)" | tr [:lower:] [:upper:])) $(eval HEADER := "$(shell man ${SECTION} intro | head -1 | sed -e 's/^[^[:blank:]]*[[:blank:]]*//' -e 's/[[:blank:]]*[^[:blank:]]*$$//' )") $(PANDOC) -standalone -M title=${TITLE} -M section=${SECTION} -M header=${HEADER} -M footer=${PANDOC_ORG} -M "date=`date +\"%B %Y\"`" --to man $< >> $@ # workaround for boldface rendering sed -i -e 's/\\f\[CR\]/\\f\[CB\]/g' $@ || true echo "$@ manpage updated" >&2 else echo "${PANDOC} is not available. Manpage $@ cannot be updated" >&2 endif vuos-0.9.2/man/fusenull.1000066400000000000000000000047661476575172100152440ustar00rootroot00000000000000.\" Copyright (C) 2023 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "FUSENULL" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME fusenull, vufusenull \- mount a null file system using FUSE and vufuse .SH SYNOPSIS \f[CB]fusenull\f[R] [\f[CB]\-hVdfs\f[R]] [\f[CB]\-o\f[R] \f[I]options\f[R] ] \f[I]dir\f[R] .PP in a \f[CB]umvu\f[R] session: .PP \f[CB]mount \-t vufusenull\f[R] [\f[CB]\-o\f[R] \f[I]options\f[R] ] \f[I]dir\f[R] .SH DESCRIPTION \f[CB]fusenull\f[R] mounts a null filesystem on \f[I]dir\f[R]. A null file system appears as an empty directory, any operation on it fails. It is generally used as a minimal FUSE module for testing. .PP \f[CB]vufusenull\f[R] is the VUOS/vufuse submodule of \f[CB]fusenull\f[R] .SH OPTIONS \f[CB]fusenull\f[R] is build upon FUSE (Filesystem in Userspace) library. the complete set of available options depends upon the specific FUSE installed. Execute \f[CB]fusenull \-h\f[R] to retrieve the actual complete list. .SS general options .TP \f[CB]\-o\f[R] opt,[opt\&...] FUSE and file specific mount options. .TP \f[CB]\-h\f[R] display a usage and options summary .TP \f[CB]\-V\f[R] \ \f[CB]\-\-version\f[R] display version .SS main FUSE mount options These options are not valid in VUOS/vufuse. .TP \f[CB]\-d\f[R] \ \f[CB]\-o debug\f[R] enable debug output (implies \-f) .TP \f[CB]\-f\f[R] foreground operation .TP \f[CB]\-s\f[R] disable multi\-threaded operation .SH SEE ALSO \f[CB]fuse\f[R](8), \f[CB]umvu\f[R](1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli. vuos-0.9.2/man/fusenull.1.md000066400000000000000000000042211476575172100156250ustar00rootroot00000000000000 # NAME fusenull, vufusenull - mount a null file system using FUSE and vufuse # SYNOPSIS `fusenull` [`-hVdfs`] [`-o` _options_ ] *dir* in a `umvu` session: `mount -t vufusenull` [`-o` _options_ ] *dir* # DESCRIPTION `fusenull` mounts a null filesystem on *dir*. A null file system appears as an empty directory, any operation on it fails. It is generally used as a minimal FUSE module for testing. `vufusenull` is the VUOS/vufuse submodule of `fusenull` # OPTIONS `fusenull` is build upon FUSE (Filesystem in Userspace) library. the complete set of available options depends upon the specific FUSE installed. Execute `fusenull -h` to retrieve the actual complete list. ### general options `-o` opt,[opt...] : FUSE and file specific mount options. `-h` : display a usage and options summary `-V`   `--version` : display version ### main FUSE mount options These options are not valid in VUOS/vufuse. `-d`   `-o debug` : enable debug output (implies -f) `-f` : foreground operation `-s` : disable multi-threaded operation # SEE ALSO `fuse`(8), `umvu`(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli. vuos-0.9.2/man/fusereal.1000066400000000000000000000047241476575172100152070ustar00rootroot00000000000000.\" Copyright (C) 2023 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "FUSEREAL" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME fusereal, vufusereal \- mount a file system tree to another place using FUSE and vufuse .SH SYNOPSIS \f[CB]fusereal\f[R] [\f[CB]\-hVdfs\f[R]] [\f[CB]\-o\f[R] \f[I]options\f[R] ] \f[I]olddir\f[R] \f[I]newdir\f[R] .PP in a \f[CB]umvu\f[R] session: .PP \f[CB]mount \-t vufusereal\f[R] [\f[CB]\-o\f[R] \f[I]options\f[R] ] \f[I]olddir\f[R] \f[I]newdir\f[R] .SH DESCRIPTION \f[CB]fusereal\f[R] causes the contents of \f[I]olddir\f[R] to be accessible (also) under \f[I]newdir\f[R]. .PP \f[CB]vufusereal\f[R] is the VUOS/vufuse submodule of \f[CB]fusereal\f[R] .SH OPTIONS \f[CB]fusereal\f[R] is build upon FUSE (Filesystem in Userspace) library. the complete set of available options depends upon the specific FUSE installed. Execute \f[CB]fusereal \-h\f[R] to retrieve the actual complete list. .SS general options .TP \f[CB]\-o\f[R] opt,[opt\&...] FUSE and file specific mount options. .TP \f[CB]\-h\f[R] display a usage and options summary .TP \f[CB]\-V\f[R] \ \f[CB]\-\-version\f[R] display version .SS main FUSE mount options These options are not valid in VUOS/vufuse. .TP \f[CB]\-d\f[R] \ \f[CB]\-o debug\f[R] enable debug output (implies \-f) .TP \f[CB]\-f\f[R] foreground operation .TP \f[CB]\-s\f[R] disable multi\-threaded operation .SH SEE ALSO \f[CB]fuse\f[R](8), \f[CB]umvu\f[R](1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli. vuos-0.9.2/man/fusereal.1.md000066400000000000000000000041301476575172100155750ustar00rootroot00000000000000 # NAME fusereal, vufusereal - mount a file system tree to another place using FUSE and vufuse # SYNOPSIS `fusereal` [`-hVdfs`] [`-o` _options_ ] *olddir* *newdir* in a `umvu` session: `mount -t vufusereal` [`-o` _options_ ] *olddir* *newdir* # DESCRIPTION `fusereal` causes the contents of *olddir* to be accessible (also) under *newdir*. `vufusereal` is the VUOS/vufuse submodule of `fusereal` # OPTIONS `fusereal` is build upon FUSE (Filesystem in Userspace) library. the complete set of available options depends upon the specific FUSE installed. Execute `fusereal -h` to retrieve the actual complete list. ### general options `-o` opt,[opt...] : FUSE and file specific mount options. `-h` : display a usage and options summary `-V`   `--version` : display version ### main FUSE mount options These options are not valid in VUOS/vufuse. `-d`   `-o debug` : enable debug output (implies -f) `-f` : foreground operation `-s` : disable multi-threaded operation # SEE ALSO `fuse`(8), `umvu`(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli. vuos-0.9.2/man/umvu.1000066400000000000000000000324031476575172100143700ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "UMVU" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]umvu\f[R] \[en] user\-mode implementation of VUOS .SH SYNOPSIS \f[CB]umvu\f[R] [\f[I]options\f[R] \&...] \f[I]command\f[R] [\f[I]args\f[R]] .SH DESCRIPTION VUOS: view based operating system. VUOS gives processes their own view of the system resources. VUOS is a different perspective on namespaces, anykernels and related concepts. The main idea behind VUOS is that it is possible to give processes their own \f[I]view\f[R] using partial virtual machines. .PP A partial virtual machine intercepts the system call requests and operates like a filter: system call can be forwarded to the kernel of the hosting system or processed by the partial virtual machine hypervisor. .PP In this way processes can see a mix of resources provided by the kernel (on which they have the same view of the other processes) and virtual resource. It is possible to mount filesystems, load networking stacks, change the structure of the file system tree, create virtual devices, etc. .PP \f[CB]umvu\f[R] is a user mode implementation of VUOS concepts It is a modular partial virtual machine. Before loading any module, \f[CB]umvu\f[R] is completely transparent, a process behaves inside \f[CB]umvu\f[R] as it would have behaved outside. \f[I]vu_insmod\f[R](1) is the tool to load modules, e.g.\ \f[I]vufuse\f[R] for file system virtualization, \f[I]vunet\f[R] for networking, \f[I]vudev\f[R] for virtual devices, etc. These are just some examples of modules provided by the VirtualSquare team: modules are dynamic library based plugins so can be designed and implemented independently and loaded/unloaded at run time. .PP \f[CB]umvu\f[R] (VUOS hypervisor) is just a user process so, while it gives new perspective for processes, it does not widen the attack surface of the kernel. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .TP \f[CB]\-x\f[R], \f[CB]\-\-nonesting\f[R] \f[CB]umvu\f[R] provides nested virtualization. It means that the system call requests of the modules can refer to virtual resource. (for example it is possible to mount a file system stored on a virtual device). The \f[CB]\-x\f[R] or \f[CB]\-\-nonesting\f[R] option disables this feature. .TP \f[CB]\-S\f[R], \f[CB]\-\-noseccomp\f[R] \f[CB]umvu\f[R] uses a seccomp filter to speed\-up the virtualization. The \f[CB]\-S\f[R] or \f[CB]\-\-noseccomp\f[R] option disables this feature. .TP \f[CB]\-f\f[R] \f[I]file\f[R], \f[CB]\-\-rc\f[R] \f[I]file\f[R] Execute commands from file instead of the system wide initialization file \f[I]/etc/vurc\f[R] and the standard personal initialization file \f[I]\[ti]/.vurc\f[R] .TP \f[CB]\-N\f[R], \f[CB]\-\-norc\f[R] Do not run the system wide initialization file \f[I]/etc/vurc\f[R] and the standard personal initialization file \f[I]\[ti]/.vurc\f[R] .TP \f[CB]\-V\f[R] \f[I]name\f[R], \f[CB]\-\-vu_name\f[R] \f[I]name\f[R] set the name of the view. This name can be read and set using \f[CB]vuname\f[R](1) .TP \f[CB]\-d\f[R] \f[I]debugtags\f[R], \f[CB]\-\-debugtags\f[R] \f[I]debugtags\f[R] set the debug tags enabled from the beginning. e.g.\ \f[CB]\-d s\f[R] enables log messages of system call requests. (see \f[CB]vudebug\f[R](1) for a detailed description) .TP \f[CB]\-D\f[R] \f[I]colorspec\f[R], \f[CB]\-\-debugcols\f[R] \f[I]colorspec\f[R] set color and font effects for log messages. e.g.\ \f[CB]\-D p:r \-D c:g#\f[R] means that path resolution messages will be displayed in red while module choice log will be in reverse green. Color definition can refer to enable and disabled categories (the option defines the colors for a successive activation). (see \f[CB]vudebug\f[R](1) for a detailed description) .TP \f[CB]\-l\f[R] \f[I]level\f[R], \f[CB]\-\-loglevel\f[R] \f[I]level\f[R] set the debug level. Only messages having level less or equal of the parameter of this option will be displayed. (0 = emergency, 1 = alert, 2 = critical, 3 = error, 4 = warning, 5 = notice, 6 = info, 7 = debug). The default level is 4. .TP \f[CB]\-s\f[R] \f[I]level\f[R], \f[CB]\-\-syslog\f[R] \f[I]level\f[R] use \f[CB]syslog\f[R](2) for logging and log messages having level less or equal of the parameter of this option. (0 = emergency, 1 = alert, 2 = critical, 3 = error, 4 = warning, 5 = notice, 6 = info, 7 = debug). This option is disabled by default. .SH EXAMPLES .SS mount a file system image The following example mounts /tmp/linux.img on /mnt. .PP start the hypervisor, and run a bash \f[I]inside\f[R] the partial virtual machine .IP .EX $ umvu bash .EE .PP This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference .IP .EX $ PS1=\[aq]\[rs]$\[rs]$ \[aq]\[ga] .EE .PP let us load vufuse: a user\-mode implementation of FUSE (source compatible with FUSE modules) .IP .EX $$ vu_insmod vufuse .EE .PP nothing is currently mounted on /mnt .IP .EX $$ ls /mnt .EE .PP the following command mounts the filesystem image /tmp/linux.img .IP .EX $$ vumount \-t vufuseext2 \-o ro /tmp/linux.img /mnt .EE .PP now the image has been mounted: .IP .EX $$ ls /mnt bin boot dev etc lib lost+found mnt proc sbin tmp usr $$ vuumount /mnt $$ ls /mnt $$ exit .EE .PP We have left the partial virtual machine .PP Comments: user can \f[I]mount\f[R] any filesystem they like, on any directory. The linux kernel is not involved for all the system calls related to files in the mounted filesystem. The effects of this \f[I]mount\f[R] is just \f[I]perceived\f[R] by the processes running in the partial virtual machine. \f[CB]vumount\f[R] is just a wrapper to the \f[CB]mount\f[R](1) system call (the command \f[CB]mount(8)\f[R] does much much more, it is setuid root and requires real uid to be root to permit filesystem mounting (\f[CB]mount\f[R](8) works in \f[CB]umvu\f[R] adding a module of uid/gid virtualization). .SS create a disk image, partition it, create a filesystem and mount it In this example an 1GiB empty file is seen as a virtual disk, this disk gets partitioned (GPT), an ext4 file system is created on its first partition and finally this file system is mounted on /mnt .PP start the hypervisor, and run a bash \f[I]inside\f[R] the partial virtual machine .IP .EX $ umvu bash .EE .PP This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference .IP .EX $ PS1=\[aq]\[rs]$\[rs]$ \[aq] .EE .PP let us load vudev and vufuse: vudev to virtualize devices and vufuse as in the previous example .IP .EX $$ vu_insmod vudev vufuse .EE .PP create a 1 GiB large empty file .IP .EX $$ truncate \-s 1G /tmp/disk $$ ls \-l /tmp/disk \-rw\-r\-\-r\-\- 1 renzo renzo 1073741824 Jun 3 11:55 /tmp/disk .EE .PP let us mount the empty file as a partitioned virtual disk: .IP .EX $$ vumount \-t vudevpartx /tmp/disk /dev/hda Bad MBR signature 0 0 .EE .PP clearly if not a partitioned disk, yet. Let us add a partitioning scheme: .IP .EX $$ /sbin/gdisk /dev/hda GPT fdisk (gdisk) version 1.0.3 Partition table scan: MBR: not present BSD: not present APM: not present GPT: not present Creating new GPT entries. Command (? for help): n Partition number (1\-128, default 1): First sector (34\-2097118, default = 2048) or {+\-}size{KMGTP}: Last sector (2048\-2097118, default = 2097118) or {+\-}size{KMGTP}: +200M Current type is \[aq]Linux filesystem\[aq] Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to \[aq]Linux filesystem\[aq] Command (? for help): n Partition number (2\-128, default 2): First sector (34\-2097118, default = 411648) or {+\-}size{KMGTP}: Last sector (411648\-2097118, default = 2097118) or {+\-}size{KMGTP}: Current type is \[aq]Linux filesystem\[aq] Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to \[aq]Linux filesystem\[aq] Command (? for help): p Disk /dev/hda: 2097152 sectors, 1024.0 MiB Sector size (logical): 512 bytes Disk identifier (GUID): F2A76123\-73ED\-4052\-BAFE\-6B37473E6187 Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 2097118 Partitions will be aligned on 2048\-sector boundaries Total free space is 2014 sectors (1007.0 KiB) Number Start (sector) End (sector) Size Code Name 1 2048 411647 200.0 MiB 8300 Linux filesystem 2 411648 2097118 823.0 MiB 8300 Linux filesystem Command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): Y OK; writing new GUID partition table (GPT) to /dev/hda. The operation has completed successfully. The disk has been partitioned: $$ ls \-l /dev/hda1 brw\-\-\-\-\-\-\- 0 renzo renzo 0, 1 Jan 1 1970 /dev/hda1 $$ ls \-l /dev/hda2 brw\-\-\-\-\-\-\- 0 renzo renzo 0, 2 Jan 1 1970 /dev/hda2 .EE .PP Now it is possible to create an ext4 partition on /dev/hda1 .IP .EX $$ /sbin/mkfs.ext4 /dev/hda1 mke2fs 1.45.1 (12\-May\-2019) warning: Unable to get device geometry for /dev/hda1 Creating filesystem with 204800 1k blocks and 51200 inodes Filesystem UUID: c96c6499\-40cd\-43df\-addf\-52e06d7e6842 Superblock backups stored on blocks: 8193, 24577, 40961, 57345, 73729 Allocating group tables: done Writing inode tables: done Creating journal (4096 blocks): done Writing superblocks and filesystem accounting information: done .EE .PP now the file system on /dev/hda1 can be mounted on /mnt .IP .EX $$ vumount \-t vufuseext2 \-o rw+ /dev/hda1 /mnt .EE .PP add a significative file on /mnt .IP .EX $$ echo ciao * /mnt/hello $$ ls \-l /mnt total 13 \-rw\-r\-\-r\-\- 1 renzo renzo 5 Jun 3 12:09 hello drwx\-\-\-\-\-\- 2 root root 12288 Jun 3 12:06 lost+found $$ vuumount /mnt $$ vuumount /dev/hda $$ exit $ .EE .SS mount a user\-level networking stack It is possible to provide network partial virtualization using the \f[CB]vunet\f[R] module .PP start the hypervisor, and run a bash \f[I]inside\f[R] the partial virtual machine .IP .EX $ umvu bash .EE .PP This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference .IP .EX $ PS1=\[aq]\[rs]$\[rs]$ \[aq] .EE .PP let us load vunet .IP .EX $$ vu_insmod vunet .EE .PP the following command #mounts# a vde network on /dev/net/myvde. (see https://github.com/rd235/vdeplug4) .IP .EX $$ vumount \-t vunetvdestack vxvde:// /dev/net/myvde .EE .PP vustack is the command to select the stack to use. .IP .EX $$ vustack /dev/net/myvde ip link 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST* mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff .EE .PP without vustack I can still access the stack provided by the linux kernel .IP .EX $$ ip link 1: lo: *LOOPBACK,UP,LOWER_UP* mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: *BROADCAST,MULTICAST,UP,LOWER_UP* mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 80:aa:bb:cc:dd:ee brd ff:ff:ff:ff:ff:ff .EE .PP let us start a bash using /dev/net/myvde as itsdfault net .IP .EX $$ vustack /dev/net/myvde bash $ PS1=\[aq]\[rs]$N\[rs]$ \[aq] .EE .PP let us configure the net .IP .EX $N$ ip addr add 192.168.250.250/24 dev vde0 $N$ ip link set vde0 up $N$ ip route add default via 192.168.250.1 $N$ ip addr 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST,UP,LOWER_UP* mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff inet 192.168.250.250/24 scope global vde0 valid_lft forever preferred_lft forever inet6 fe80::7c76:c0ff:fed7:3b37/64 scope link valid_lft forever preferred_lft forever $N$ ip route default via 192.168.250.1 dev vde0 192.168.250.0/24 dev vde0 proto kernel scope link src 192.168.250.250 $N$ ping 80.80.80.80 PING 80.80.80.80 (80.80.80.80) 56(84) bytes of data. 64 bytes from 80.80.80.80: icmp_seq=1 ttl=52 time=56.9 ms 64 bytes from 80.80.80.80: icmp_seq=2 ttl=52 time=57.9 ms \[ha]C $N$ .EE .SH SEE ALSO vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vumount(1), vuumount(1), vudebug(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/umvu.1.md000066400000000000000000000304241476575172100147700ustar00rootroot00000000000000 # NAME `umvu` -- user-mode implementation of VUOS # SYNOPSIS `umvu` [*options* ...] *command* [*args*] # DESCRIPTION VUOS: view based operating system. VUOS gives processes their own view of the system resources. VUOS is a different perspective on namespaces, anykernels and related concepts. The main idea behind VUOS is that it is possible to give processes their own *view* using partial virtual machines. A partial virtual machine intercepts the system call requests and operates like a filter: system call can be forwarded to the kernel of the hosting system or processed by the partial virtual machine hypervisor. In this way processes can see a mix of resources provided by the kernel (on which they have the same view of the other processes) and virtual resource. It is possible to mount filesystems, load networking stacks, change the structure of the file system tree, create virtual devices, etc. `umvu` is a user mode implementation of VUOS concepts It is a modular partial virtual machine. Before loading any module, `umvu` is completely transparent, a process behaves inside `umvu` as it would have behaved outside. *vu_insmod*(1) is the tool to load modules, e.g. *vufuse* for file system virtualization, *vunet* for networking, *vudev* for virtual devices, etc. These are just some examples of modules provided by the VirtualSquare team: modules are dynamic library based plugins so can be designed and implemented independently and loaded/unloaded at run time. `umvu` (VUOS hypervisor) is just a user process so, while it gives new perspective for processes, it does not widen the attack surface of the kernel. # OPTIONS `-h`, `--help` : Print a short help message and exit. `-x`, `--nonesting` : `umvu` provides nested virtualization. It means that the system call requests : of the modules can refer to virtual resource. (for example it is possible to mount : a file system stored on a virtual device). : The `-x` or `--nonesting` option disables this feature. `-S`, `--noseccomp` : `umvu` uses a seccomp filter to speed-up the virtualization. : The `-S` or `--noseccomp` option disables this feature. `-f` *file*, `--rc` *file* : Execute commands from file instead of the system wide initialization file : */etc/vurc* and the standard personal initialization file *~/.vurc* `-N`, `--norc` : Do not run the system wide initialization file : */etc/vurc* and the standard personal initialization file *~/.vurc* `-V` *name*, `--vu_name` *name* : set the name of the view. This name can be read and set using `vuname`(1) `-d` *debugtags*, `--debugtags` *debugtags* : set the debug tags enabled from the beginning. e.g. `-d s` enables : log messages of system call requests. (see `vudebug`(1) for a detailed : description) `-D` *colorspec*, `--debugcols` *colorspec* : set color and font effects for log messages. e.g. `-D p:r -D c:g#` : means that path resolution messages will be displayed in red while : module choice log will be in reverse green. Color definition can refer to : enable and disabled categories (the option defines the colors for : a successive activation). (see `vudebug`(1) for a detailed : description) `-l` *level*, `--loglevel` *level* : set the debug level. Only messages having level less or equal of : the parameter of this option will be displayed. : (0 = emergency, 1 = alert, 2 = critical, 3 = error, 4 = warning, 5 = notice, 6 = info, 7 = debug). : The default level is 4. `-s` *level*, `--syslog` *level* : use `syslog`(2) for logging and log messages having level less or equal of : the parameter of this option. : (0 = emergency, 1 = alert, 2 = critical, 3 = error, 4 = warning, 5 = notice, 6 = info, 7 = debug). : This option is disabled by default. # EXAMPLES ### mount a file system image ### The following example mounts /tmp/linux.img on /mnt. start the hypervisor, and run a bash *inside* the partial virtual machine ``` $ umvu bash ``` This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference ``` $ PS1='\$\$ '` ``` let us load vufuse: a user-mode implementation of FUSE (source compatible with FUSE modules) ``` $$ vu_insmod vufuse ``` nothing is currently mounted on /mnt ``` $$ ls /mnt ``` the following command mounts the filesystem image /tmp/linux.img ``` $$ vumount -t vufuseext2 -o ro /tmp/linux.img /mnt ``` now the image has been mounted: ``` $$ ls /mnt bin boot dev etc lib lost+found mnt proc sbin tmp usr $$ vuumount /mnt $$ ls /mnt $$ exit ``` We have left the partial virtual machine Comments: user can *mount* any filesystem they like, on any directory. The linux kernel is not involved for all the system calls related to files in the mounted filesystem. The effects of this *mount* is just *perceived* by the processes running in the partial virtual machine. `vumount` is just a wrapper to the `mount`(1) system call (the command `mount(8)` does much much more, it is setuid root and requires real uid to be root to permit filesystem mounting (`mount`(8) works in `umvu` adding a module of uid/gid virtualization). ### create a disk image, partition it, create a filesystem and mount it ### In this example an 1GiB empty file is seen as a virtual disk, this disk gets partitioned (GPT), an ext4 file system is created on its first partition and finally this file system is mounted on /mnt start the hypervisor, and run a bash *inside* the partial virtual machine ``` $ umvu bash ``` This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference ``` $ PS1='\$\$ ' ``` let us load vudev and vufuse: vudev to virtualize devices and vufuse as in the previous example ``` $$ vu_insmod vudev vufuse ``` create a 1 GiB large empty file ``` $$ truncate -s 1G /tmp/disk $$ ls -l /tmp/disk -rw-r--r-- 1 renzo renzo 1073741824 Jun 3 11:55 /tmp/disk ``` let us mount the empty file as a partitioned virtual disk: ``` $$ vumount -t vudevpartx /tmp/disk /dev/hda Bad MBR signature 0 0 ``` clearly if not a partitioned disk, yet. Let us add a partitioning scheme: ``` $$ /sbin/gdisk /dev/hda GPT fdisk (gdisk) version 1.0.3 Partition table scan: MBR: not present BSD: not present APM: not present GPT: not present Creating new GPT entries. Command (? for help): n Partition number (1-128, default 1): First sector (34-2097118, default = 2048) or {+-}size{KMGTP}: Last sector (2048-2097118, default = 2097118) or {+-}size{KMGTP}: +200M Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem' Command (? for help): n Partition number (2-128, default 2): First sector (34-2097118, default = 411648) or {+-}size{KMGTP}: Last sector (411648-2097118, default = 2097118) or {+-}size{KMGTP}: Current type is 'Linux filesystem' Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem' Command (? for help): p Disk /dev/hda: 2097152 sectors, 1024.0 MiB Sector size (logical): 512 bytes Disk identifier (GUID): F2A76123-73ED-4052-BAFE-6B37473E6187 Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 2097118 Partitions will be aligned on 2048-sector boundaries Total free space is 2014 sectors (1007.0 KiB) Number Start (sector) End (sector) Size Code Name 1 2048 411647 200.0 MiB 8300 Linux filesystem 2 411648 2097118 823.0 MiB 8300 Linux filesystem Command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): Y OK; writing new GUID partition table (GPT) to /dev/hda. The operation has completed successfully. The disk has been partitioned: $$ ls -l /dev/hda1 brw------- 0 renzo renzo 0, 1 Jan 1 1970 /dev/hda1 $$ ls -l /dev/hda2 brw------- 0 renzo renzo 0, 2 Jan 1 1970 /dev/hda2 ``` Now it is possible to create an ext4 partition on /dev/hda1 ``` $$ /sbin/mkfs.ext4 /dev/hda1 mke2fs 1.45.1 (12-May-2019) warning: Unable to get device geometry for /dev/hda1 Creating filesystem with 204800 1k blocks and 51200 inodes Filesystem UUID: c96c6499-40cd-43df-addf-52e06d7e6842 Superblock backups stored on blocks: 8193, 24577, 40961, 57345, 73729 Allocating group tables: done Writing inode tables: done Creating journal (4096 blocks): done Writing superblocks and filesystem accounting information: done ``` now the file system on /dev/hda1 can be mounted on /mnt ``` $$ vumount -t vufuseext2 -o rw+ /dev/hda1 /mnt ``` add a significative file on /mnt ``` $$ echo ciao * /mnt/hello $$ ls -l /mnt total 13 -rw-r--r-- 1 renzo renzo 5 Jun 3 12:09 hello drwx------ 2 root root 12288 Jun 3 12:06 lost+found $$ vuumount /mnt $$ vuumount /dev/hda $$ exit $ ``` ### mount a user-level networking stack ### It is possible to provide network partial virtualization using the `vunet` module start the hypervisor, and run a bash *inside* the partial virtual machine ``` $ umvu bash ``` This is the prompt of the partial virtualized shell, let us change it to $$ to show the difference ``` $ PS1='\$\$ ' ``` let us load vunet ``` $$ vu_insmod vunet ``` the following command #mounts# a vde network on /dev/net/myvde. (see https://github.com/rd235/vdeplug4) ``` $$ vumount -t vunetvdestack vxvde:// /dev/net/myvde ``` vustack is the command to select the stack to use. ``` $$ vustack /dev/net/myvde ip link 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST* mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff ``` without vustack I can still access the stack provided by the linux kernel ``` $$ ip link 1: lo: *LOOPBACK,UP,LOWER_UP* mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: *BROADCAST,MULTICAST,UP,LOWER_UP* mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 80:aa:bb:cc:dd:ee brd ff:ff:ff:ff:ff:ff ``` let us start a bash using /dev/net/myvde as itsdfault net ``` $$ vustack /dev/net/myvde bash $ PS1='\$N\$ ' ``` let us configure the net ``` $N$ ip addr add 192.168.250.250/24 dev vde0 $N$ ip link set vde0 up $N$ ip route add default via 192.168.250.1 $N$ ip addr 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST,UP,LOWER_UP* mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000 link/ether 7e:76:c0:d7:3b:37 brd ff:ff:ff:ff:ff:ff inet 192.168.250.250/24 scope global vde0 valid_lft forever preferred_lft forever inet6 fe80::7c76:c0ff:fed7:3b37/64 scope link valid_lft forever preferred_lft forever $N$ ip route default via 192.168.250.1 dev vde0 192.168.250.0/24 dev vde0 proto kernel scope link src 192.168.250.250 $N$ ping 80.80.80.80 PING 80.80.80.80 (80.80.80.80) 56(84) bytes of data. 64 bytes from 80.80.80.80: icmp_seq=1 ttl=52 time=56.9 ms 64 bytes from 80.80.80.80: icmp_seq=2 ttl=52 time=57.9 ms ^C $N$ ``` # SEE ALSO vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vumount(1), vuumount(1), vudebug(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_insmod.1000066400000000000000000000036511476575172100154020ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VU_INSMOD" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vu_insmod\f[R] \[en] user\-mode implementation of VUOS .SH SYNOPSIS \f[CB]vu_insmod\f[R] [\f[I]options\f[R] \&...] \f[I]vu_module\f[R] [\f[I]vu_module\f[R]] .SH DESCRIPTION \f[I]This is a VUOS command. It works only inside a vuos virtual namespace\f[R] see \f[CB]umvu\f[R](1). .PP This command adds one or more modules to umvu. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .TP \f[CB]\-p\f[R], \f[CB]\-\-permanent\f[R] permanently inserted modules cannot be removed. .SH EXAMPLE The following command adds vufuse (file system virtualization), vudev (virtual devices) and vunet (virtual networking). .IP .EX vu_insmod vufuse vudev vunet .EE .SH SEE ALSO umvu(1), vu_lsmod(1), vu_rmmod(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_insmod.1.md000066400000000000000000000032771476575172100160050ustar00rootroot00000000000000 # NAME `vu_insmod` -- user-mode implementation of VUOS # SYNOPSIS `vu_insmod` [*options* ...] *vu_module* [*vu_module*] # DESCRIPTION *This is a VUOS command. It works only inside a vuos virtual namespace* see `umvu`(1). This command adds one or more modules to umvu. # OPTIONS `-h`, `--help` : Print a short help message and exit. `-p`, `--permanent` : permanently inserted modules cannot be removed. # EXAMPLE The following command adds vufuse (file system virtualization), vudev (virtual devices) and vunet (virtual networking). ``` vu_insmod vufuse vudev vunet ``` # SEE ALSO umvu(1), vu_lsmod(1), vu_rmmod(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_lsmod.1000066400000000000000000000034621476575172100152270ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VU_LSMOD" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vu_lsmod\f[R] \[en] user\-mode implementation of VUOS .SH SYNOPSIS \f[CB]vu_lsmod\f[R] [\f[I]options\f[R] \&...] \f[I]vu_module\f[R] [\f[I]vu_module\f[R]] .SH DESCRIPTION \f[I]This is a VUOS command. It works only inside a vuos virtual namespace\f[R] see \f[CB]umvu\f[R](1). .PP This command lists the modules currently loaded. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .SH EXAMPLE .IP .EX $ vu_lsmod vufuse vufuse: vu virtual file systems (user level FUSE) vudev: vu virtual devices vunet: vu virtual networking .EE .SH SEE ALSO umvu(1), vu_insmod(1), vu_rmmod(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_lsmod.1.md000066400000000000000000000031331476575172100156210ustar00rootroot00000000000000 # NAME `vu_lsmod` -- user-mode implementation of VUOS # SYNOPSIS `vu_lsmod` [*options* ...] *vu_module* [*vu_module*] # DESCRIPTION *This is a VUOS command. It works only inside a vuos virtual namespace* see `umvu`(1). This command lists the modules currently loaded. # OPTIONS `-h`, `--help` : Print a short help message and exit. # EXAMPLE ``` $ vu_lsmod vufuse vufuse: vu virtual file systems (user level FUSE) vudev: vu virtual devices vunet: vu virtual networking ``` # SEE ALSO umvu(1), vu_insmod(1), vu_rmmod(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_rmmod.1000066400000000000000000000034641476575172100152310ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VU_RMMOD" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vu_rmmod\f[R] \[en] user\-mode implementation of VUOS .SH SYNOPSIS \f[CB]vu_rmmod\f[R] [\f[I]options\f[R] \&...] \f[I]vu_module\f[R] [\f[I]vu_module\f[R]] .SH DESCRIPTION \f[I]This is a VUOS command. It works only inside a vuos virtual namespace\f[R] see \f[CB]umvu\f[R](1). .PP This command removes one or more modules currently loaded in umvu. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .SH EXAMPLE The following command removes vudev (virtual devices) and vunet (virtual networking). .IP .EX vu_rmmod vudev vunet .EE .SH SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vu_rmmod.1.md000066400000000000000000000031401476575172100156170ustar00rootroot00000000000000 # NAME `vu_rmmod` -- user-mode implementation of VUOS # SYNOPSIS `vu_rmmod` [*options* ...] *vu_module* [*vu_module*] # DESCRIPTION *This is a VUOS command. It works only inside a vuos virtual namespace* see `umvu`(1). This command removes one or more modules currently loaded in umvu. # OPTIONS `-h`, `--help` : Print a short help message and exit. # EXAMPLE The following command removes vudev (virtual devices) and vunet (virtual networking). ``` vu_rmmod vudev vunet ``` # SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vudebug.1000066400000000000000000000102661476575172100150400ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUDEBUG" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vudebug\f[R] \[en] debug utility for umvu .SH SYNOPSIS \f[CB]vudebug\f[R] \[en]help .PP \f[CB]vudebug\f[R] [\f[I]debarg\f[R] [\f[I]debarg\f[R] \&...]] [ \[en] \f[I]command\f[R] [\f[I]args\f[R]]] .PP where \f[I]debarg\f[R] has the following syntax: .PP \f[CB]+\f[R][\f[I]tag\f[R][\f[I]tag\f[R] \&...]][\f[CB]:\f[R]\f[I]colorspec\f[R]] .PP or .PP \f[CB]\-\f[R][\f[I]tag\f[R][\f[I]tag\f[R] \&...]] .PP or .PP \f[CB]?\f[R][\f[I]tag\f[R][\f[I]tag\f[R] \&...]] .PP and \f[I]colorspec\f[R] is a combination of the following characters: \f[CB]nwrgbcmyNWRGBCMY+\-_*#\f[R] .SH DESCRIPTION vudebug enables or disables debug log messages. Log messages are classified into categories. Each category is identified by a \f[I]tag\f[R] (one alphanumeric character). By convention lowercase letters are for logging messages of the hypervisor, while capital letters are for modules. .PP When \f[CB]vudebug\f[R] command line ends with \f[CB]\-\-\f[R] followed by a command and its command line arguments, logging is enabled for the execution of that command (and for all the subprocesses it eventually creates). Otherwise \f[CB]vudebug\f[R] changes the categories to log globally, for all the processes. .SH OPTIONS .TP \f[CB]\-\-help\f[R] Print a short help message and exit. .TP \f[I]debarg\f[R] each debug argument begins by \f[CB]+\f[R], \f[CB]\-\f[R] or \f[CB]?\f[R] followed by zero, one or more debug tags. (\f[CB]+\f[R] enables log messages, \f[CB]\-\f[R] disables log messages, \f[CB]?\f[R] check if the log messages are enabled). When the debug argument has no tags, it is applied to all the tags. Log messages of different categories can be shown in different colors and font effects. When \f[CB]vudebug\f[R] is used to enable/re\-enable tags (\f[CB]+\f[R]) each \f[I]debarg\f[R] can be followed by a semicolon (\f[CB]:\f[R]) and a color specification. A color specification is a string composed by the following characters: .TP \f[CB]\f[R] \f[CB]n w r g b c m y\f[R]: set foreground color (black, white, red, green, blue, cyan, magenta or yellow) .TP \f[CB]\f[R] \f[CB]N W R G B C M Y\f[R]: set background color (black, white, red, green, blue, cyan, magenta or yellow) .TP \f[CB]\f[R] \f[CB]+ \- _ * #\f[R]: font effect (bright, dim,underlined, blinking, reverse video). .SH EXAMPLES Get a list of available logging categories: .IP .EX $ vudebug ? D \- VUDEV F \- VUFUSE N \- VUNET a \- ACTION c \- CHOICE f \- FILETABLE m \- MODULE n \- NESTED p \- PATH s \- SYSCALL v \- VNODE .EE .PP the list may vary depending on the version of the hypervisor and the modules currently loaded. .PP Enable path resolution logging: .IP .EX $ vudebug +p .EE .PP List some categories to see which ones are active: .IP .EX $ vudebug ?ps p + PATH s \- SYSCALL .EE .PP PATH is active, SYSCALL is not active. .PP Disable all the categories: .IP .EX $ vudebug \- .EE .PP Launch a bash and log syscall requests in red, path resolution in bold\-blue, module choice in reverse green: .IP .EX $ vudebug +s:r +p:b+ +c:g# \-\- bash .EE .SH SEE ALSO umvu(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vudebug.1.md000066400000000000000000000071721476575172100154410ustar00rootroot00000000000000 # NAME `vudebug` -- debug utility for umvu # SYNOPSIS `vudebug` --help `vudebug` [*debarg* [*debarg* ...]] [ -- *command* [*args*]] where *debarg* has the following syntax: `+`[*tag*[*tag* ...]][`:`*colorspec*] or `-`[*tag*[*tag* ...]] or `?`[*tag*[*tag* ...]] and *colorspec* is a combination of the following characters: `nwrgbcmyNWRGBCMY+-_*#` # DESCRIPTION vudebug enables or disables debug log messages. Log messages are classified into categories. Each category is identified by a *tag* (one alphanumeric character). By convention lowercase letters are for logging messages of the hypervisor, while capital letters are for modules. When `vudebug` command line ends with `--` followed by a command and its command line arguments, logging is enabled for the execution of that command (and for all the subprocesses it eventually creates). Otherwise `vudebug` changes the categories to log globally, for all the processes. # OPTIONS `--help` : Print a short help message and exit. *debarg* : each debug argument begins by `+`, `-` or `?` followed by zero, one or more : debug tags. (`+` enables log messages, `-` disables log messages, `?` check if the : log messages are enabled). When the debug argument has no tags, it is applied to all : the tags. : Log messages of different categories can be shown in different colors and font effects. : When `vudebug` is used to enable/re-enable tags (`+`) each *debarg* can be followed by a : semicolon (`:`) and a color specification. A color specification is a string composed by the : following characters: ` ` : `n w r g b c m y`: set foreground color (black, white, red, green, blue, cyan, magenta or yellow) ` ` : `N W R G B C M Y`: set background color (black, white, red, green, blue, cyan, magenta or yellow) ` ` : `+ - _ * #`: font effect (bright, dim,underlined, blinking, reverse video). # EXAMPLES Get a list of available logging categories: ``` $ vudebug ? D - VUDEV F - VUFUSE N - VUNET a - ACTION c - CHOICE f - FILETABLE m - MODULE n - NESTED p - PATH s - SYSCALL v - VNODE ``` the list may vary depending on the version of the hypervisor and the modules currently loaded. Enable path resolution logging: ``` $ vudebug +p ``` List some categories to see which ones are active: ``` $ vudebug ?ps p + PATH s - SYSCALL ``` PATH is active, SYSCALL is not active. Disable all the categories: ``` $ vudebug - ``` Launch a bash and log syscall requests in red, path resolution in bold-blue, module choice in reverse green: ``` $ vudebug +s:r +p:b+ +c:g# -- bash ``` # SEE ALSO umvu(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vumount.1000066400000000000000000000071301476575172100151100ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUMOUNT" "1" "November 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vumount\f[R] \[en] mount a filesystem or a resource .SH SYNOPSIS \f[CB]vumount\f[R] [\f[I]options\f[R] \&...] \f[I]source\f[R] \f[I]destination\f[R] .SH DESCRIPTION Everything is (or can be seen) as a file. It is part of the philosophy of UNIX. The file hierarchy is the global naming facility. .PP VUOS follows this principle: VUOS modules use \f[CB]mount\f[R](2) not only to mount virtual filesystems but also to activate other virtual services. The mountpoint, \f[I]destination\f[R] in the synopsis, is the name that will be used to identify the virtual entity/service. .PP For example, in \f[CB]vudev\f[R] it is possible to mount devices, in \f[CB]vunet\f[R] the mountpoint is the name of the networking stack, \f[CB]vustack\f[R](1) uses the path of the mountpoint to set the current stack for processes. .PP \f[CB]vumount\f[R] is just a command interface to \f[CB]mount\f[R](2). The \f[CB]mount\f[R](8) command is a complex tool which includes several features like the management of /etc/fstab and /etc/mtab. \f[CB]mount\f[R](8) is a root setuid executable and performs security checks before the actual \f[CB]mount\f[R](2) syscall request. \f[CB]mount\f[R](8) can be used in VUOS in place of \f[CB]vumount\f[R] but it requires the (virtual) real uid of the executing process to be 0 (root). e.g.\ \f[CB]vusu\f[R](1) can be used to set the virtual real uid to 0. \f[CB]vumount\f[R] has been designed for VUOS but can be used to run the \f[CB]mount\f[R] system call directly, without all the other management actions provided by \f[CB]mount\f[R](8). \f[CB]vumount\f[R] is not setuid root. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .TP \f[CB]\-o\f[R] \f[I]list\f[R], \f[CB]\-\-options\f[R] \f[I]list\f[R] comma\-separated list of mount options .TP \f[CB]\-t\f[R] \f[I]fstype\f[R], \f[CB]\-\-types\f[R] \f[I]fstype\f[R] define the filesystem type .TP \f[CB]\-r\f[R], \f[CB]\-\-read\-only\f[R] mount the filesystem read\-only (same as \-o ro) .TP \f[CB]\-w\f[R], \f[CB]\-\-rw\f[R], \f[CB]\-\-read\-write\f[R] mount the filesystem read\-write (default) .TP \f[CB]\-B\f[R], \f[CB]\-\-bind\f[R] mount a subtree somewhere else (same as \-o bind) .TP \f[CB]\-M\f[R], \f[CB]\-\-move\f[R] move a subtree to some other place .TP \f[CB]\-R\f[R], \f[CB]\-\-rbind\f[R] mount a subtree and all submounts somewhere else .SH SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vuumount(1), vudebug(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vumount.1.md000066400000000000000000000060711476575172100155120ustar00rootroot00000000000000 # NAME `vumount` -- mount a filesystem or a resource # SYNOPSIS `vumount` [*options* ...] *source* *destination* # DESCRIPTION Everything is (or can be seen) as a file. It is part of the philosophy of UNIX. The file hierarchy is the global naming facility. VUOS follows this principle: VUOS modules use `mount`(2) not only to mount virtual filesystems but also to activate other virtual services. The mountpoint, *destination* in the synopsis, is the name that will be used to identify the virtual entity/service. For example, in `vudev` it is possible to mount devices, in `vunet` the mountpoint is the name of the networking stack, `vustack`(1) uses the path of the mountpoint to set the current stack for processes. `vumount` is just a command interface to `mount`(2). The `mount`(8) command is a complex tool which includes several features like the management of /etc/fstab and /etc/mtab. `mount`(8) is a root setuid executable and performs security checks before the actual `mount`(2) syscall request. `mount`(8) can be used in VUOS in place of `vumount` but it requires the (virtual) real uid of the executing process to be 0 (root). e.g. `vusu`(1) can be used to set the virtual real uid to 0. `vumount` has been designed for VUOS but can be used to run the `mount` system call directly, without all the other management actions provided by `mount`(8). `vumount` is not setuid root. # OPTIONS `-h`, `--help` : Print a short help message and exit. `-o` *list*, `--options` *list* : comma-separated list of mount options `-t` *fstype*, `--types` *fstype* : define the filesystem type `-r`, `--read-only` : mount the filesystem read-only (same as -o ro) `-w`, `--rw`, `--read-write` : mount the filesystem read-write (default) `-B`, `--bind` : mount a subtree somewhere else (same as -o bind) `-M`, `--move` : move a subtree to some other place `-R`, `--rbind` : mount a subtree and all submounts somewhere else # SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vuumount(1), vudebug(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vuname.1000066400000000000000000000065631476575172100146770ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUNAME" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vuname\f[R] \[en] print system/view information \- set view name. .SH SYNOPSIS \f[CB]vuname\f[R] [\f[I]options\f[R] \&...] .PP or .PP \f[CB]vuname\f[R] \f[I]newname\f[R] .SH DESCRIPTION Print certain system/view information. With no options, and no arguments, same as \-s. With no options and exactly one argument: set the VUOS view name. .PP This tool extends \f[CB]uname\f[R](1). It should provide the same output of \f[CB]uname\f[R] except when it runs as a VUOS process (or if \f[CB]\-\-x\f[R]/\f[CB]\-\-nouname\f[R] disables this feature). In VUOS the system information is extended with view information. .SH OPTIONS .TP \f[CB]\-a\f[R], \f[CB]\-\-all\f[R] print all information, in the following order, except omit \f[CB]\-p\f[R] and \f[CB]\-i\f[R] if unknown. .TP \f[CB]\-s\f[R], \f[CB]\-\-kernel\-name\f[R] print the kernel name .TP \f[CB]\-n\f[R], \f[CB]\-\-nodename\f[R] print the network node hostname .TP \f[CB]\-r\f[R], \f[CB]\-\-kernel\-release\f[R] print the kernel release .TP \f[CB]\-v\f[R], \f[CB]\-\-kernel\-version\f[R] print the kernel version .TP \f[CB]\-m\f[R], \f[CB]\-\-machine\f[R] print the machine hardware name .TP \f[CB]\-p\f[R], \f[CB]\-\-processor\f[R] print the processor type or \[lq]unknown\[rq] .TP \f[CB]\-i\f[R], \f[CB]\-\-hardware\-platform\f[R] print the hardware platform or \[lq]unknown\[rq] .TP \f[CB]\-o\f[R], \f[CB]\-\-operating\-system\f[R] print the operating system .TP \f[CB]\-U\f[R], \f[CB]\-\-serverid\f[R] print the VUOS server id (it is the process id of the hypervisor) .TP \f[CB]\-V\f[R], \f[CB]\-\-viewname\f[R] print the view name .TP \f[CB]\-P\f[R], \f[CB]\-\-prompt\f[R] return a suitable (shell) command prompt: the nodename (\f[CB]\-n\f[R]) if \f[CB]vuname\f[R] runs outside VUOS else the view name (\f[CB]\-V\f[R]) if it has been defined otherwise the nodename followedby the server id enclosed in square brackets (something like \f[I]host[42]\f[R]). .TP \f[CB]\-x\f[R], \f[CB]\-\-nouname\f[R] do not use uname (without this flag the command behaves like uname when it runs on a non VUOS enabled environment). .TP \f[CB]\-q\f[R], \f[CB]\-\-quiet\f[R] quiet mode: error messages suppressed .TP \f[CB]\-\-help\f[R] display a help message and exit .TP \f[CB]\-\-version\f[R] output version information and exit vuos-0.9.2/man/vuname.1.md000066400000000000000000000054441476575172100152730ustar00rootroot00000000000000 # NAME `vuname` -- print system/view information - set view name. # SYNOPSIS `vuname` [*options* ...] or `vuname` *newname* # DESCRIPTION Print certain system/view information. With no options, and no arguments, same as -s. With no options and exactly one argument: set the VUOS view name. This tool extends `uname`(1). It should provide the same output of `uname` except when it runs as a VUOS process (or if `--x`/`--nouname` disables this feature). In VUOS the system information is extended with view information. # OPTIONS `-a`, `--all` : print all information, in the following order, except omit `-p` and `-i` if unknown. `-s`, `--kernel-name` : print the kernel name `-n`, `--nodename` : print the network node hostname `-r`, `--kernel-release` : print the kernel release `-v`, `--kernel-version` : print the kernel version `-m`, `--machine` : print the machine hardware name `-p`, `--processor` : print the processor type or "unknown" `-i`, `--hardware-platform` : print the hardware platform or "unknown" `-o`, `--operating-system` : print the operating system `-U`, `--serverid` : print the VUOS server id (it is the process id of the hypervisor) `-V`, `--viewname` : print the view name `-P`, `--prompt` : return a suitable (shell) command prompt: the nodename (`-n`) if `vuname` runs outside VUOS else the view name (`-V`) if it has been defined otherwise the nodename followedby the server id enclosed in square brackets (something like *host[42]*). `-x`, `--nouname` : do not use uname (without this flag the command behaves like uname when it runs on a non VUOS enabled environment). `-q`, `--quiet` : quiet mode: error messages suppressed `--help` : display a help message and exit `--version` : output version information and exit vuos-0.9.2/man/vustack.1000066400000000000000000000067051476575172100150620ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUSTACK" "1" "November 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vustack\f[R] \[en] set the default networking stack .SH SYNOPSIS \f[CB]vustack\f[R] [\f[I]options\f[R] \&...] \f[I]stack\f[R] \f[I]command\f[R] [\f[I]args\f[R]] .SH DESCRIPTION \f[CB]vunet\f[R] is the VUOS module for networking virtualization. Networking stacks can be loaded using \f[CB]vumount\f[R](1) and are identified by a pathname: the mount point. \f[CB]vustack\f[R] selects the stack to use among those available; \f[I]command\f[R] runs using the stack selected \f[CB]vustack\f[R]. .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. If combined with \f[CB]\-v\f[R] print also the list of protocol family names. .TP \f[CB]\-s\f[R], \f[CB]\-\-supported\f[R] select the stack only for the protocol families supported by \f[I]stack\f[R]. .TP \f[CB]\-f\f[R] \f[I]list\f[R], \f[CB]\-\-family\f[R] \f[I]list\f[R], \f[CB]\-\-families\f[R] \f[I]list\f[R] select the stack for the protocol families in \f[I]list\f[R]. \f[I]list\f[R] is a comma separated list of protocol names or numbers. .TP \f[CB]\-v\f[R], \f[CB]\-\-verbose\f[R] print the list of protocol families object of the stack selection. .SH EXAMPLES Load \f[CB]vunet\f[R] and mount a stack: .IP .EX $ vu_insmod vunet $ vumount \-t vunetvdestack vde:// /dev/net/vde .EE .PP Run \f[I]ip link\f[R] using the stack mounted in /dev/net/vde: .IP .EX $ vustack /dev/net/vde ip link 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST* mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 5a:1e:97:fa:ab:a3 brd ff:ff:ff:ff:ff:ff .EE .PP Run \f[I]ip link set vde0 up\f[R] selecting /dev/net/vde only for the families supported by vunetvdestack: .IP .EX $ vustack \-s \-v /dev/net/vde ip link set vde0 up Using /dev/net/vde for the following address families: inet(2) inet6(10) netlink(16) packet(17) .EE .PP mount a null stack and use it to disable netlink: .IP .EX $ vumount \-t vunetnull vde:// /dev/net/null $ exec vustack \-f netlink \-v /dev/net/null bash Using /dev/net/null for the following address families: netlink(16) $ ip addr Cannot open netlink socket: Address family not supported by protocol .EE .SH SEE ALSO umvu(1), vumount(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vustack.1.md000066400000000000000000000060371476575172100154570ustar00rootroot00000000000000 # NAME `vustack` -- set the default networking stack # SYNOPSIS `vustack` [*options* ...] *stack* *command* [*args*] # DESCRIPTION `vunet` is the VUOS module for networking virtualization. Networking stacks can be loaded using `vumount`(1) and are identified by a pathname: the mount point. `vustack` selects the stack to use among those available; *command* runs using the stack selected `vustack`. # OPTIONS `-h`, `--help` : Print a short help message and exit. If combined with `-v` print also : the list of protocol family names. `-s`, `--supported` : select the stack only for the protocol families supported by *stack*. `-f` *list*, `--family` *list*, `--families` *list* : select the stack for the protocol families in *list*. *list* is a : comma separated list of protocol names or numbers. `-v`, `--verbose` : print the list of protocol families object of the stack selection. # EXAMPLES Load `vunet` and mount a stack: ``` $ vu_insmod vunet $ vumount -t vunetvdestack vde:// /dev/net/vde ``` Run *ip link* using the stack mounted in /dev/net/vde: ``` $ vustack /dev/net/vde ip link 1: lo: *LOOPBACK* mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: vde0: *BROADCAST,MULTICAST* mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 5a:1e:97:fa:ab:a3 brd ff:ff:ff:ff:ff:ff ``` Run *ip link set vde0 up* selecting /dev/net/vde only for the families supported by vunetvdestack: ``` $ vustack -s -v /dev/net/vde ip link set vde0 up Using /dev/net/vde for the following address families: inet(2) inet6(10) netlink(16) packet(17) ``` mount a null stack and use it to disable netlink: ``` $ vumount -t vunetnull vde:// /dev/net/null $ exec vustack -f netlink -v /dev/net/null bash Using /dev/net/null for the following address families: netlink(16) $ ip addr Cannot open netlink socket: Address family not supported by protocol ``` # SEE ALSO umvu(1), vumount(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vusu.1000066400000000000000000000073531476575172100144040ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUSU" "1" "November 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vusu\f[R] \[en] set the default networking stack .SH SYNOPSIS \f[CB]vusu\f[R] [\f[I]options\f[R]] [\-] [\f[I]user\f[R] [*argment\&...]] .SH DESCRIPTION \f[CB]vusu\f[R] allows one to run commands with a substitute user and group ID (in VUOS). It requires a module loaded in VUOS able to redefine uid/gid, e.g.\ \f[CB]unrealuidgid\f[R]. .PP When called without arguments, \f[CB]vusu\f[R] defaults to running an interactive shell as (virtual) \f[I]root\f[R]. .PP For compatibility with \f[CB]su\f[R](1), \f[CB]vusu\f[R] defaults to not change the current directory and to only set the environment variables \f[CB]HOME\f[R] and \f[CB]SHELL\f[R] (plus \f[CB]USER\f[R] and \f[CB]LOGNAME\f[R] if the target user is not root). It is recommended to always use the \f[CB]\-\-login\f[R] option (instead of its shortcut \-) to avoid side effects caused by mixing environments. .SH OPTIONS .TP \f[CB]\-c\f[R] \f[I]command\f[R], \f[CB]\-\-command\f[R] \f[I]command\f[R] Pass command to the shell with the \f[CB]\-c\f[R] option. .TP \f[CB]\-\f[R], \f[CB]\-l\f[R], \f[CB]\-\-login\f[R] Start the shell as a login shell with an environment similar to a real login: .PD 0 .P .PD .TP \f[CB]\f[R] \- clears all the environment variables except \f[CB]TERM\f[R] .PD 0 .P .PD .TP \f[CB]\f[R] \- initializes the environment variables \f[CB]HOME\f[R], \f[CB]SHELL\f[R], \f[CB]USER\f[R], \f[CB]LOGNAME\f[R], and \f[CB]PATH\f[R] .PD 0 .P .PD .TP \f[CB]\f[R] \- changes to the target user\[cq]s home directory .PD 0 .P .PD .TP \f[CB]\f[R] \- sets argv[0] of the shell to `\-' in order to make the shell a login shell .TP \f[CB]\-m\f[R], \f[CB]\-p\f[R], \f[CB]\-\-preserve\-environment\f[R] Preserve the entire environment, i.e.\ it does not set \f[CB]HOME\f[R], \f[CB]SHELL\f[R], \f[CB]USER\f[R] nor \f[CB]LOGNAME\f[R]. This option is ignored if the option \f[CB]\-\-login\f[R] is specified. .TP \f[CB]\-s\f[R] \f[I]shell\f[R], \f[CB]\-\-shell\f[R] \f[I]shell\f[R] Run the specified shell instead of the default. The shell to run is selected according to the following rules, in order: .TP \f[CB]\f[R] \- the shell specified with \f[CB]\-\-shell\f[R] .PD 0 .P .PD .TP \f[CB]\f[R] \- the shell specified in the environment variable \f[CB]SHELL\f[R], if the \f[CB]\-\-preserve\-environment\f[R] option is used .PD 0 .P .PD .TP \f[CB]\f[R] \- the shell listed in the passwd entry of the target user .PD 0 .P .PD .TP \f[CB]\f[R] \- /bin/sh .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Display a short help message and exit. .SH SEE ALSO umvu(1), su(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli. (most of this man page is a derivative work from \f[CB]su\f[R](1) man page) vuos-0.9.2/man/vusu.1.md000066400000000000000000000060521476575172100147760ustar00rootroot00000000000000 # NAME `vusu` -- set the default networking stack # SYNOPSIS `vusu` [*options*] [-] [*user* [*argment...]] # DESCRIPTION `vusu` allows one to run commands with a substitute user and group ID (in VUOS). It requires a module loaded in VUOS able to redefine uid/gid, e.g. `unrealuidgid`. When called without arguments, `vusu` defaults to running an interactive shell as (virtual) *root*. For compatibility with `su`(1), `vusu` defaults to not change the current directory and to only set the environment variables `HOME` and `SHELL` (plus `USER` and `LOGNAME` if the target user is not root). It is recommended to always use the `--login` option (instead of its shortcut -) to avoid side effects caused by mixing environments. # OPTIONS `-c` *command*, `--command` *command* : Pass command to the shell with the `-c` option. `-`, `-l`, `--login` : Start the shell as a login shell with an environment similar to a real login:\ ` ` : \- clears all the environment variables except `TERM`\ ` ` : \- initializes the environment variables `HOME`, `SHELL`, `USER`, `LOGNAME`, and `PATH`\ ` ` : \- changes to the target user's home directory\ ` ` : \- sets argv[0] of the shell to '-' in order to make the shell a login shell `-m`, `-p`, `--preserve-environment` : Preserve the entire environment, i.e. it does not set `HOME`, `SHELL`, `USER` nor `LOGNAME`. : This option is ignored if the option `--login` is specified. `-s` *shell*, `--shell` *shell* : Run the specified shell instead of the default. The shell to run is selected according to the following : rules, in order: ` ` : \- the shell specified with `--shell`\ ` ` : \- the shell specified in the environment variable `SHELL`, if the `--preserve-environment` option is used\ ` ` : \- the shell listed in the passwd entry of the target user\ ` ` : \- /bin/sh `-h`, `--help` : Display a short help message and exit. # SEE ALSO umvu(1), su(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli. (most of this man page is a derivative work from `su`(1) man page) vuos-0.9.2/man/vuumount.1000066400000000000000000000040651476575172100153010ustar00rootroot00000000000000.\" Copyright (C) 2019 VirtualSquare. Project Leader: Renzo Davoli .\" .\" This is free documentation; 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. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual 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 manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, .\" MA 02110-1301 USA. .\" .\" Automatically generated by Pandoc 3.1.11 .\" .TH "VUUMOUNT" "1" "January 2024" "VirtualSquare\-VUOS" "General Commands Manual" .SH NAME \f[CB]vuumount\f[R] \[en] unmount a filesystem or a resource .SH SYNOPSIS \f[CB]vuumount\f[R] [\f[I]options\f[R] \&...] \f[I]source\f[R] \f[I]destination\f[R] .SH DESCRIPTION \f[CB]vuumount\f[R] is just a command interface to \f[CB]umount\f[R](2). It has been designed for VUOS but can be used to run the \f[CB]umount\f[R] system call directly, without all the other management actions provided by \f[CB]umount\f[R](8). \f[CB]vuumount\f[R] is not setuid root (viceversa \f[CB]umount\f[R] is setuid root). .SH OPTIONS .TP \f[CB]\-h\f[R], \f[CB]\-\-help\f[R] Print a short help message and exit. .TP \f[CB]\-f\f[R], \f[CB]\-\-force\f[R] force unmount (e.g.\ in case of an unreachable NFS system) .TP \f[CB]\-d\f[R], \f[CB]\-\-detach\-loop\f[R] if mounted loop device, also free this loop device .SH SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vumount(1), udebug(1) .SH AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/man/vuumount.1.md000066400000000000000000000034301476575172100156730ustar00rootroot00000000000000 # NAME `vuumount` -- unmount a filesystem or a resource # SYNOPSIS `vuumount` [*options* ...] *source* *destination* # DESCRIPTION `vuumount` is just a command interface to `umount`(2). It has been designed for VUOS but can be used to run the `umount` system call directly, without all the other management actions provided by `umount`(8). `vuumount` is not setuid root (viceversa `umount` is setuid root). # OPTIONS `-h`, `--help` : Print a short help message and exit. `-f`, `--force` : force unmount (e.g. in case of an unreachable NFS system) `-d`, `--detach-loop` : if mounted loop device, also free this loop device # SEE ALSO umvu(1), vu_insmod(1), vu_lsmod(1), vu_rmmod(1), vumount(1), udebug(1) # AUTHOR VirtualSquare. Project leader: Renzo Davoli vuos-0.9.2/scripts/000077500000000000000000000000001476575172100142245ustar00rootroot00000000000000vuos-0.9.2/scripts/CMakeLists.txt000066400000000000000000000040151476575172100167640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(VU_SYSCALL_CONF ${PROJECT_SOURCE_DIR}/vu_syscalls.conf) set(VU_SCRIPT_PATH ${PROJECT_SOURCE_DIR}/scripts) add_custom_target(Dynamic_Sources ALL DEPENDS ${VU_SYSCALL_DEFS} ${VU_ARCHTABLE} ${SYSCALL_NR_COMPAT_H} ${VU_SYSNAMES} ${VU_SYSTABLE} ${R_TABLE_H}) add_custom_command(OUTPUT ${VU_DYN_HEADER_PATH} COMMAND mkdir ${VU_DYN_HEADER_PATH}) add_custom_command(OUTPUT ${VU_DYN_SOURCE_PATH} COMMAND mkdir ${VU_DYN_SOURCE_PATH}) execute_process(COMMAND ${VU_SCRIPT_PATH}/syscall_deps.sh ${CMAKE_C_COMPILER} OUTPUT_VARIABLE SYSCALL_DEPS) # syscall_defs.h add_custom_command(OUTPUT ${VU_SYSCALL_DEFS} COMMAND ${VU_SCRIPT_PATH}/syscall_defs_gen.py ${VU_SYSCALL_CONF} > ${VU_SYSCALL_DEFS} COMMENT "Populating VU syscall list" DEPENDS ${VU_SYSCALL_CONF} ${VU_DYN_HEADER_PATH}) # r_table.h add_custom_command(OUTPUT ${R_TABLE_H} COMMAND ${VU_SCRIPT_PATH}/r_table_gen.sh ${CMAKE_C_COMPILER} ${VU_SCRIPT_PATH} > ${R_TABLE_H} COMMENT "Generating r_table" DEPENDS ${VU_DYN_HEADER_PATH} ${SYSCALL_DEPS}) # syscall_nr_compat_gen.h add_custom_command(OUTPUT ${SYSCALL_NR_COMPAT_H} COMMAND ${VU_SCRIPT_PATH}/syscall_nr_compat_gen.py ${VU_SYSCALL_CONF} > ${SYSCALL_NR_COMPAT_H} COMMENT "Generating syscall NR compat list" DEPENDS ${VU_SYSCALL_CONF} ${VU_DYN_HEADER_PATH}) # syscall_names.c add_custom_command(OUTPUT ${VU_SYSNAMES} COMMAND ${VU_SCRIPT_PATH}/syscall_names_gen.sh ${CMAKE_C_COMPILER} ${VU_SCRIPT_PATH} > ${VU_SYSNAMES} COMMENT "Generating syscall names table" DEPENDS ${VU_DYN_SOURCE_PATH} ${SYSCALL_DEPS}) # arch_table.c add_custom_command(OUTPUT ${VU_ARCHTABLE} COMMAND ${VU_SCRIPT_PATH}/archtable_gen.py ${ARCH} ${VU_SYSCALL_CONF} > ${VU_ARCHTABLE} COMMENT "Generating architecture table" DEPENDS ${VU_SYSCALL_CONF} ${VU_DYN_SOURCE_PATH}) # syscall_table.c add_custom_command(OUTPUT ${VU_SYSTABLE} COMMAND ${VU_SCRIPT_PATH}/syscall_table_gen.py ${VU_SYSCALL_CONF} > ${VU_SYSTABLE} COMMENT "Generating VU syscall table" DEPENDS ${VU_SYSCALL_CONF} ${VU_DYN_SOURCE_PATH}) vuos-0.9.2/scripts/archtable_gen.py000077500000000000000000000035661476575172100173710ustar00rootroot00000000000000#!/usr/bin/env python3 import sys from os import path, chdir def usage(): print(f"{sys.argv[0]}: the input should be vu_syscalls.conf") if len(sys.argv) < 2 or not path.isfile(sys.argv[1]): usage() sys.exit(1) # Parse vu_syscalls.conf vu_syscalls = dict() vu_sysargs = dict() vvu_sysargs = dict() with open(sys.argv[1]) as f: for line in f: line = line.strip() if not line.startswith('#'): um_syscall_list = line.split(':') if len(um_syscall_list) > 1: um_syscall = um_syscall_list[0].split(',') value = um_syscall[0].strip() value = value.split('/')[0].strip() if value.startswith('-'): value = value[1:].strip() for s in um_syscall: sys_arg = s.split('/') vsysname = sys_arg[0].strip() if vsysname.startswith('-'): vsysname = vsysname[1:].strip() if len(sys_arg) > 1: vvu_sysargs[vsysname] = sys_arg[1].strip() else: for s in um_syscall: vu_syscalls[s.split('/')[0].strip()] = value for s in um_syscall: sys_arg = s.split('/') if len(sys_arg) > 1: vu_sysargs[sys_arg[0].strip()] = sys_arg[1].strip() # Parse and output footer = "};\n" print ('''#include #include /*This table has been autogenerated! */ const uint16_t vu_arch_table[SYSCALL_NR_OVERESTIMATION] = {''') for syscall in sorted(vu_syscalls): print(f"\t#ifdef __NR_{syscall}") print(f"\t\t[__NR_{syscall}] = __VU_{vu_syscalls[syscall]},") print("\t#endif") print(footer); print('const uint8_t vu_arch_args[SYSCALL_NR_OVERESTIMATION] = {') for syscall in sorted(vu_sysargs): print(f"\t#ifdef __NR_{syscall}") print(f"\t\t[__NR_{syscall}] = 0{vu_sysargs[syscall]},") print("\t#endif") print(footer); print('''const uint8_t vvu_arch_args[VVU_NR_SYSCALLS] = { \t[0] = 0,''') for syscall in sorted(vvu_sysargs): print(f"\t[-__VVU_{syscall}] = 0{vvu_sysargs[syscall]},") print(footer); vuos-0.9.2/scripts/r_table_gen.py000077500000000000000000000011561476575172100170450ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os syscall_names = [] for line in sys.stdin: fields = line.split() if len(fields) == 2 and fields[0] == '#define': name = fields[1] if name[:5] == '__NR_': syscall_names.append(name[5:]) print( '''#ifndef R_TABLE_H #define R_TABLE_H /* THIS FILE HAS BEEN AUTOMATICALLY GENERATED, DO NOT EDIT */ #include #include extern long (*native_syscall)(); ''') for f in sorted(syscall_names): print(f'#define r_{f}(...) native_syscall(__NR_{f}, ## __VA_ARGS__)') print( ''' #include #include #endif'''); vuos-0.9.2/scripts/r_table_gen.sh000077500000000000000000000001141476575172100170200ustar00rootroot00000000000000#!/bin/sh echo "#include" | $1 -dN -E - | $2/r_table_gen.py vuos-0.9.2/scripts/syscall_defs_gen.py000077500000000000000000000030631476575172100201070ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os.path if len(sys.argv) < 2 or not os.path.isfile(sys.argv[1]): print(f"{sys.argv[0]}: the input should be 'vu_syscalls.conf'") sys.exit(1) # Parse and output code = '''#ifndef __VU_SYSCALL_DEFS__ #define __VU_SYSCALL_DEFS__ /* Arch independent definitions */ ''' vu_syscall_max_namelen = 0 vvu_syscall_max_namelen = 0 with open(sys.argv[1]) as f: counter = 0 vcounter = 1 mcounter = 0 for line in f: line = line.strip() if line == "BUILTIN": mcounter = counter elif not line.startswith('#'): syscall_line = line.split(':') if len(syscall_line) > 1: syscall = syscall_line[0].strip() if len(syscall) > 1: syscall = syscall.split(',')[0].strip() syscall = syscall.split('/')[0] if syscall.startswith('-'): syscall = syscall[1:].strip() syscall_len = len(syscall) if syscall_len > vvu_syscall_max_namelen: vvu_syscall_max_namelen = syscall_len code += f"#define __VVU_{syscall} {-vcounter}\n" vcounter += 1 else: syscall_len = len(syscall) if syscall_len > vu_syscall_max_namelen: vu_syscall_max_namelen = syscall_len code += f"#define __VU_{syscall} {counter}\n" counter += 1 if mcounter == 0: mcounter = counter code += f"\n#define VU_NR_SYSCALLS {counter}" code += f"\n#define VU_NR_MODULE_SYSCALLS {mcounter}" code += f"\n#define VVU_NR_SYSCALLS {vcounter}" code += f"\n#define VU_SYSCALL_MAX_NAMELEN {vu_syscall_max_namelen}" code += f"\n#define VVU_SYSCALL_MAX_NAMELEN {vvu_syscall_max_namelen}" code += "\n\n#endif" print(code) vuos-0.9.2/scripts/syscall_deps.sh000077500000000000000000000005611476575172100172520ustar00rootroot00000000000000#!/bin/sh # compute and print on stdout all the (nested_ dependencies of "#include" echo "#include" | gcc -M -E - | \ sed ':a; N; s/\n/ /; ta' | sed 's/^-: *//;s/ *$//;s/\\//g;s/ */;/g' # sed magics: first sed=join all the lines # second sed: delete leading -: and trailing spaces, delete all \ and change any sequence of spaces to ; vuos-0.9.2/scripts/syscall_names_gen.py000077500000000000000000000024411476575172100202700ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os syscall_names = [] for line in sys.stdin: fields = line.split() if len(fields) == 3 and fields[0] == '#define': name = fields[1] if name[:5] == '__NR_': syscall_names.insert(0, name[5:]) print('''#include #include struct syscallname { int sysno; char *syscall_name; struct syscallname *next; }; static struct syscallname syscallname_table[] = {''') for name in syscall_names: print(f'''#ifdef __NR_{name} {{__NR_{name}, "{name}", NULL}}, #endif''') print('};') print(''' #define SYSCALLNAME_TABLE_LEN (sizeof(syscallname_table) / sizeof(*syscallname_table)) #define SYSCALLNAME_HASHMASK 255 static struct syscallname *syscall_name_hash[SYSCALLNAME_HASHMASK + 1]; const char *syscallname(int sysno) { int key = sysno & SYSCALLNAME_HASHMASK; struct syscallname *scan; for (scan = syscall_name_hash[key]; scan != NULL; scan = scan->next) if (sysno == scan->sysno) return scan->syscall_name; return "unknown"; } __attribute__((constructor)) static void init (void) { unsigned int i; for (i = 0; i < SYSCALLNAME_TABLE_LEN; i++) { int key = syscallname_table[i].sysno & SYSCALLNAME_HASHMASK; syscallname_table[i].next = syscall_name_hash[key]; syscall_name_hash[key] = &syscallname_table[i]; } }''') vuos-0.9.2/scripts/syscall_names_gen.sh000077500000000000000000000001221476575172100202440ustar00rootroot00000000000000#!/bin/sh echo "#include" | $1 -dD -E - | $2/syscall_names_gen.py vuos-0.9.2/scripts/syscall_nr_compat_gen.py000077500000000000000000000031131476575172100211440ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os ''' generate fake __NR_xxxx constants for syscalls not defined in the current arch so that code like: if (... == __NR_xxxx) or switch (...) { case __NR_xxxx is just skipped. No arch #ifdef are required. ''' __NR__first = 1000000000 if len(sys.argv) < 2 or not os.path.isfile(sys.argv[1]): print(f"{sys.argv[0]}: the input should be 'vu_syscalls.conf'") sys.exit(1) # Parse and output def acceptable(string): if string.startswith('#') or \ string.startswith('-') or \ string.startswith('null') or \ string.startswith('BUILTIN'): return False else: return True def get_syscall_names(string): syscall_list = [] s = string.rpartition(':') if ':' == s[1]: seq = s[0].split(', ') for syscall in seq: parts = syscall.rpartition('/') if parts[1] == '/': syscall_list.append(parts[0]) else: syscall_list.append(parts[2]) return syscall_list syscall_list = [] with open(sys.argv[1]) as f: for line in f: if acceptable(line): syscall_list += get_syscall_names(line) print('''#ifndef __SYSCALL_NR_COMPAT_H #define __SYSCALL_NR_COMPAT_H /* Generate compat __NR for missing system calls */ ''') for syscall_nr,syscall_def in enumerate(syscall_list, start = __NR__first): print(f'''#ifndef __NR_{syscall_def} # define __NR_{syscall_def} {syscall_nr} #endif\n''') print(f'#define __NR__first {__NR__first}') print(f'#define __NR__last {__NR__first + len(syscall_list) - 1}') print('#define __NR__is_unsupp(X) ((X) >= __NR__first && (X) <= __NR__last)'); print('\n\n#endif') vuos-0.9.2/scripts/syscall_table_gen.py000077500000000000000000000041271476575172100202570ustar00rootroot00000000000000#!/usr/bin/env python3 import sys import os.path def usage(): print(f"{sys.argv[0]}: the input should be 'vu_syscalls.conf'") if len(sys.argv) < 2 or not os.path.isfile(sys.argv[1]): usage() sys.exit(1) # Parse and output header = ''' #include #include /* Architecture INdependent table, * this is unique and stable for UMView reference */ /*This table has been autogenerated from vu_syscalls.conf */ ''' print(header) cset = set() wiset = set() wdset = set() woset = set() vwset = set() table = "const struct syscall_tab_entry vu_syscall_table[] = {\n" vtable = "const struct vsyscall_tab_entry vvu_syscall_table[] = {\n" ntable = "const char *vu_syscall_names[] = {\n" vntable = "const char *vvu_syscall_names[] = {\n" with open(sys.argv[1]) as f: for line in f: line = line.strip() if not line.startswith('#'): linesplit = line.split(':', maxsplit = 1) if len(linesplit) > 1: s, args = linesplit s = s.split(',')[0].strip() s = s.split('/')[0].strip() if s.startswith('-'): s = s[1:].strip() stag = "__VVU_" + s args = args.split(',') c = "choice_" + args[0].strip() w = "vw_" + args[1].strip() vtable += f"\t[-{stag}] = {{{c}, {w}}},\n" vntable += f"\t[-{stag}] = \"{s}\",\n" cset.add(c) vwset.add(w) else: stag = "__VU_" + s args = args.split(',') while len(args) < 6: args.append("NULL") c = "choice_" + args[0].strip() win = "wi_" + args[1].strip() wd = "wd_" + args[2].strip() wout = "wo_" +args[3].strip() table += f"\t[{stag}] = {{{c}, {win}, {wd}, {wout}}},\n" ntable += f"\t[{stag}] = \"{s}\",\n" cset.add(c) wiset.add(win) wdset.add(wd) woset.add(wout) table += "};\n" vtable += "};\n" ntable += "};\n" vntable += "};\n" for f in sorted(cset): print(f"choicef_t {f};") for f in sorted(wiset): print(f"wrapf_t {f};") for f in sorted(wdset): print(f"wrapf_t {f};") for f in sorted(woset): print(f"wrapf_t {f};") for f in sorted(vwset): print(f"wrapf_t {f};") print() print(table) print(ntable) print(vtable) print(vntable) vuos-0.9.2/test_modules/000077500000000000000000000000001476575172100152445ustar00rootroot00000000000000vuos-0.9.2/test_modules/CMakeLists.txt000066400000000000000000000011041476575172100200000ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VU_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) foreach(VU_MOD ${VU_MODULES}) string(REPLACE ".c" "" VU_MOD_FILE ${VU_MOD}) get_filename_component(VU_MOD_TARGET ${VU_MOD_FILE} NAME) add_library(${VU_MOD_TARGET} SHARED ${VU_MOD}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) endforeach(VU_MOD) target_link_libraries(unrealinfofs vumod) vuos-0.9.2/test_modules/mountreal.c000066400000000000000000000154321476575172100174230ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(mountreal) struct vu_module_t vu_module = { .name = "mountreal", .description = "Mount mapping to FS (server side)" }; struct mountreal_entry { char *source; }; static const char *unwrap(const char *path, char *buf, size_t size) { struct mountreal_entry *entry = vu_get_ht_private_data(); snprintf(buf, size, "%s%s", entry->source, path); if (buf[0] == 0) snprintf(buf, size, "/"); // printk("unwrap *%s* -> *%s*\n", path, buf); return (buf); } int vu_mountreal_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *private) { char pathbuf[PATH_MAX]; return lstat(unwrap(pathname, pathbuf, PATH_MAX), buf); } ssize_t vu_mountreal_readlink(char *path, char *buf, size_t bufsiz) { char pathbuf[PATH_MAX]; return readlink(unwrap(path, pathbuf, PATH_MAX), buf, bufsiz); } #if 0 int vu_mountreal_access(char *path, int mode, int flags) { char pathbuf[PATH_MAX]; return faccessat(AT_FDCWD, unwrap(path, pathbuf, PATH_MAX), mode, flags); } #endif int vu_mountreal_open(const char *pathname, int flags, mode_t mode, void **private) { char pathbuf[PATH_MAX]; return open(unwrap(pathname, pathbuf, PATH_MAX), flags, mode); } int vu_mountreal_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *private) { return syscall(__NR_getdents64, fd, dirp, count); } int vu_mountreal_unlink(const char *pathname) { char pathbuf[PATH_MAX]; return unlink(unwrap(pathname, pathbuf, PATH_MAX)); } int vu_mountreal_mkdir(const char *pathname, mode_t mode) { char pathbuf[PATH_MAX]; return mkdir(unwrap(pathname, pathbuf, PATH_MAX), mode); } int vu_mountreal_rmdir(const char *pathname) { char pathbuf[PATH_MAX]; return rmdir(unwrap(pathname, pathbuf, PATH_MAX)); } int vu_mountreal_mknod(const char *pathname, mode_t mode, dev_t dev) { char pathbuf[PATH_MAX]; return mknod(unwrap(pathname, pathbuf, PATH_MAX), mode, dev); } int vu_mountreal_chmod(const char *pathname, mode_t mode, int fd, void *private) { char pathbuf[PATH_MAX]; return chmod(unwrap(pathname, pathbuf, PATH_MAX), mode); } int vu_mountreal_lchown(const char *pathname, uid_t owner, gid_t group, int fd, void *private) { char pathbuf[PATH_MAX]; return lchown(unwrap(pathname, pathbuf, PATH_MAX), owner, group); } int vu_mountreal_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags, int fd, void *private) { char pathbuf[PATH_MAX]; return utimensat(dirfd, unwrap(pathname, pathbuf, PATH_MAX), times, flags); } int vu_mountreal_symlink(const char *target, const char *linkpath) { char pathbuf[PATH_MAX]; return symlink(target, unwrap(linkpath, pathbuf, PATH_MAX)); } int vu_mountreal_link(const char *target, const char *linkpath) { char pathbuf[PATH_MAX]; char pathbuf2[PATH_MAX]; return link(unwrap(target, pathbuf, PATH_MAX), unwrap(linkpath, pathbuf2, PATH_MAX)); } int vu_mountreal_rename(const char *target, const char *linkpath, int flags) { char pathbuf[PATH_MAX]; char pathbuf2[PATH_MAX]; return rename(unwrap(target, pathbuf, PATH_MAX), unwrap(linkpath, pathbuf2, PATH_MAX)); } int vu_mountreal_truncate(const char *path, off_t length, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return truncate(unwrap(path, pathbuf, PATH_MAX), length); } int vu_mountreal_statfs(const char *path, struct statfs *buf, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return statfs(unwrap(path, pathbuf, PATH_MAX), buf); } ssize_t vu_mountreal_lgetxattr(const char *path, const char *name, void *value, size_t size, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return lgetxattr(unwrap(path, pathbuf, PATH_MAX), name, value, size); } int vu_mountreal_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return lsetxattr(unwrap(path, pathbuf, PATH_MAX), name, value, size, flags); } ssize_t vu_mountreal_llistxattr(const char *path, char *list, size_t size, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return llistxattr(unwrap(path, pathbuf, PATH_MAX), list, size); } int vu_mountreal_lremovexattr(const char *path, const char *name, int fd, void *fdprivate) { char pathbuf[PATH_MAX]; return lremovexattr(unwrap(path, pathbuf, PATH_MAX), name); } int vu_mountreal_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vu_service_t *s = vu_mod_getservice(); struct mountreal_entry *entry = malloc(sizeof(struct mountreal_entry)); const char *source_no_root = strcmp(source, "/") == 0 ? "" : source; //printk("MOUNT %s %s\n", source, target); entry->source = strdup(source_no_root); vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, entry); errno = 0; return 0; } int vu_mountreal_umount2(const char *target, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); int ret_value; if ((ret_value = vuht_del(ht, flags)) < 0) { errno = -ret_value; return -1; } return 0; } void vu_mountreal_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { if (type == CHECKPATH) { struct mountreal_entry *entry = vuht_get_private_data(ht); if (entry->source) free(entry->source); free(entry); } } void *vu_mountreal_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, close) = close; vu_syscall_handler(s, read) = read; vu_syscall_handler(s, write) = write; vu_syscall_handler(s, lseek) = lseek; vu_syscall_handler(s, pread64) = pread; vu_syscall_handler(s, pwrite64) = pwrite; vu_syscall_handler(s, fcntl) = fcntl; vu_syscall_handler(s, epoll_ctl) = epoll_ctl; #pragma GCC diagnostic pop return NULL; } int vu_mountreal_fini(void *private) { return 0; } vuos-0.9.2/test_modules/netlinkdump.c000066400000000000000000000067561476575172100177600ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(netlinkdump) struct vu_module_t vu_module = { .name = "netlinkdump", .description = "dump netlink messages" }; static struct vuht_entry_t *ht; static void dump(const char *title, const uint8_t *data, size_t bufsize, ssize_t len) { ssize_t line, i; /* out format: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 01234567890123456789012345678901234567890123456789012345678901234 */ char hexbuf[48]; char charbuf[17]; printk("%s size %zd len %zd:\n", title, bufsize, len); if (bufsize > 0 && len > 0) { for (line = 0; line < len; line += 16) { for (i = 0; i < 16; i++) { ssize_t pos = line + i; if (pos < len) { sprintf(hexbuf + (3 * i), "%02x ", data[pos]); charbuf[i] = data[pos] >= ' ' && data[pos] <= '~' ? data[pos] : '.'; } else { sprintf(hexbuf + (3 * i), " "); charbuf[i] = ' '; } } charbuf[i] = 0; printk(" %s %s\n", hexbuf, charbuf); } } } ssize_t vu_netlinkdump_sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, void *msg_control, size_t msg_controllen, void *fdprivate) { ssize_t retval = sendto(sockfd, buf, len, flags, dest_addr, addrlen); dump("->", buf, len, retval); return retval; } ssize_t vu_netlinkdump_recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen, void *msg_control, size_t *msg_controllen, void *fdprivate) { ssize_t retval = recvfrom(sockfd, buf, len,flags, src_addr, addrlen); dump("<-", buf, len, retval); return retval; } void *vu_netlinkdump_init(void) { struct vu_service_t *s = vu_mod_getservice(); int family = AF_NETLINK; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, socket) = socket; vu_syscall_handler(s, bind) = bind; vu_syscall_handler(s, connect) = connect; vu_syscall_handler(s, listen) = listen; vu_syscall_handler(s, accept4) = accept4; vu_syscall_handler(s, getsockname) = getsockname; vu_syscall_handler(s, getpeername) = getpeername; vu_syscall_handler(s, setsockopt) = setsockopt; vu_syscall_handler(s, getsockopt) = getsockopt; vu_syscall_handler(s, epoll_ctl) = epoll_ctl; vu_syscall_handler(s, close) = close; #pragma GCC diagnostic pop ht = vuht_add(CHECKSOCKET, &family, sizeof(int), s, 0, NULL, NULL); return NULL; } int vu_netlinkdump_fini(void *private) { vuht_del(ht, MNT_FORCE); return 0; } vuos-0.9.2/test_modules/unreal.c000066400000000000000000000077771476575172100167200ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* this is a test module: when loaded the entire file system "appears" as /unreal and /unreal/unreal. $ vu_insmod unreal $ ls /etc/passwd /etc/passwd $ ls /unreal/etc/passwd /unreal/etc/passwd $ ls /unreal/unreal/etc/passwd /unreal/unreal/etc/passwd $ ls /unreal/unreal/unreal/etc/passwd /unreal/unreal/unreal/etc/passwd': No such file or directory It is possible in this way to test (some of) the correctness of vuos implementation: the operation on file or dir X must have the same result when testing the same operation on /unreal/X or /unreal/unreal/X. X -> VUOS force processes to forward the syscall requests to the kernel /unreal/X -> VUOS itself forwards the requests to the kernel /unreal/unreal/X -> VUOS forwards the requests to VUOS and then to the kernel (this latter case uses process self-virtualization) */ #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unreal) struct vu_module_t vu_module = { .name = "unreal", .description = "Mapping to FS (server side)" }; int vu_unreal_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *private) { return syscall(__NR_getdents64, fd, dirp, count); } #if 0 int vu_unreal_access(char *path, int mode, int flags) { return faccessat(AT_FDCWD, path, mode, flags); } #endif static struct vuht_entry_t *ht1,*ht2; void vu_unreal_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { if (type == CHECKPATH) { //printk("%*.*s\n", arglen, arglen, arg); } } void *vu_unreal_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, lstat) = lstat; vu_syscall_handler(s, readlink) = readlink; vu_syscall_handler(s, open) = open; vu_syscall_handler(s, unlink) = unlink; vu_syscall_handler(s, mkdir) = mkdir; vu_syscall_handler(s, rmdir) = rmdir; vu_syscall_handler(s, mknod) = mknod; vu_syscall_handler(s, chmod) = chmod; vu_syscall_handler(s, lchown) = lchown; vu_syscall_handler(s, utimensat) = utimensat; vu_syscall_handler(s, symlink) = symlink; vu_syscall_handler(s, link) = link; vu_syscall_handler(s, rename) = rename; vu_syscall_handler(s, truncate) = truncate; vu_syscall_handler(s, statfs) = statfs; vu_syscall_handler(s, lgetxattr) = lgetxattr; vu_syscall_handler(s, lsetxattr) = lsetxattr; vu_syscall_handler(s, llistxattr) = llistxattr; vu_syscall_handler(s, close) = close; vu_syscall_handler(s, read) = read; vu_syscall_handler(s, write) = write; vu_syscall_handler(s, lseek) = lseek; vu_syscall_handler(s, pread64) = pread; vu_syscall_handler(s, pwrite64) = pwrite; vu_syscall_handler(s, fcntl) = fcntl; #pragma GCC diagnostic pop ht1 = vuht_pathadd(CHECKPATH,"/","/unreal","unreal",0,"",s,0,NULL,NULL); ht2 = vuht_pathadd(CHECKPATH,"/","/unreal","unreal",0,"",s,0,NULL,NULL); return NULL; } int vu_unreal_fini(void *private) { if (ht2 && vuht_del(ht2, MNT_FORCE) == 0) ht2 = NULL; if (ht1 && vuht_del(ht1, MNT_FORCE) == 0) ht1 = NULL; return 0; } vuos-0.9.2/test_modules/unrealcap.c000066400000000000000000000114741476575172100173710ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2019 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* Usage example: * $ vu_insmod unrealcap * $ /sbin/capsh --caps=cap_chown+eip -- * $ /sbin/getpcaps $$ * Capabilities for `4855': = cap_chown+eip */ #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unrealcap) struct vu_module_t vu_module = { .name = "unrealcap", .description = "virtualize capabilities" }; struct vu_cap_t { pid_t pid; struct __user_cap_data_struct data[2]; struct vu_cap_t *next; struct vu_cap_t **pprev; }; static pthread_rwlock_t vucap_lock = PTHREAD_RWLOCK_INITIALIZER; static struct vu_cap_t *vu_cap_head = NULL; static __thread struct vu_cap_t *vu_cap = NULL; static struct vu_cap_t *vu_cap_search(pid_t pid) { struct vu_cap_t *scan = vu_cap_head; while (scan != NULL) { if (scan->pid == pid) return scan; scan = scan->next; } return NULL; } int vu_unrealcap_capget(cap_user_header_t hdrp, cap_user_data_t datap) { struct vu_cap_t *this_cap = NULL; int retvalue; pthread_rwlock_rdlock(&vucap_lock); if (hdrp->pid == vu_mod_gettid()) this_cap = vu_cap; else this_cap = vu_cap_search(hdrp->pid); if (this_cap == NULL) retvalue = capget(hdrp, datap); else { memcpy(datap, this_cap->data, sizeof(this_cap->data)); retvalue = 0; } pthread_rwlock_unlock(&vucap_lock); return retvalue; } struct vu_cap_t *new_vu_cap(pid_t pid, cap_user_data_t datap) { struct vu_cap_t *this_cap = malloc(sizeof(struct vu_cap_t)); if (this_cap == NULL) return NULL; else { this_cap->pid = pid; memcpy(this_cap->data, datap, sizeof(this_cap->data)); pthread_rwlock_wrlock(&vucap_lock); this_cap->next = vu_cap_head; if (vu_cap_head != NULL) vu_cap_head->pprev = &(this_cap->next); this_cap->pprev = &vu_cap_head; vu_cap_head = this_cap; pthread_rwlock_unlock(&vucap_lock); return this_cap; } } int vu_unrealcap_capset(cap_user_header_t hdrp, cap_user_data_t datap) { if (vu_cap == NULL) { vu_cap = new_vu_cap(vu_mod_gettid(), datap); if (vu_cap == NULL) return errno = ENOMEM, -1; else return 0; } else { pthread_rwlock_wrlock(&vucap_lock); memcpy(vu_cap->data, datap, sizeof(vu_cap->data)); pthread_rwlock_unlock(&vucap_lock); return 0; } } static void *vu_cap_clone(void *arg) { if (vu_cap != NULL) { return new_vu_cap(-1, vu_cap->data); } else return NULL; } static void vu_cap_start(void *arg) { vu_cap = arg; if (vu_cap != NULL) vu_cap->pid = vu_mod_gettid(); } static void vu_cap_exec(void *arg) { //struct mod_inheritance_exec_arg *mod_exec = arg; //Future management of security capability xattr } static void vu_cap_terminate(void) { if (vu_cap != NULL) { pthread_rwlock_wrlock(&vucap_lock); if (vu_cap->next != NULL) vu_cap->next->pprev = vu_cap->pprev; *(vu_cap->pprev) = vu_cap->next; free(vu_cap); vu_cap = NULL; pthread_rwlock_unlock(&vucap_lock); } } static void *vu_cap_tracer_upcall(mod_inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case MOD_INH_CLONE: ret_value = vu_cap_clone(arg); break; case MOD_INH_START: vu_cap_start(ioarg); break; case MOD_INH_EXEC: vu_cap_exec(arg); break; case MOD_INH_TERMINATE: vu_cap_terminate(); break; } return ret_value; } static short vusc[]={ __NR_capget, __NR_capset, }; #define VUSCLEN (sizeof(vusc) / sizeof(*vusc)) static struct vuht_entry_t *ht[VUSCLEN]; void *vu_unrealcap_init(void) { struct vu_service_t *s = vu_mod_getservice(); unsigned int i; for (i = 0; i < VUSCLEN; i++) { int vu_syscall = vu_arch_table[vusc[i]]; ht[i] = vuht_add(CHECKSC, &vu_syscall, sizeof(int), s, 0, NULL, NULL); } mod_inheritance_upcall_register(vu_cap_tracer_upcall); return NULL; } int vu_unrealcap_fini(void *private) { unsigned int i; for (i = 0; i < VUSCLEN; i++) { if (ht[i] && vuht_del(ht[i], MNT_FORCE) == 0) ht[i] = NULL; } mod_inheritance_upcall_deregister(vu_cap_tracer_upcall); return 0; } vuos-0.9.2/test_modules/unrealinfofs.c000066400000000000000000000163011476575172100201040ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli VirtualSquare team. * * 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, see . * * Tutorial module for pseudofiles and info file systems. * $ load the module * # mount -t unrealinfofs none /mnt * $ ls -l /mnt * total 0 * -r--r--r-- 0 root root 0 Jan 1 1970 date * drwxrwxrwx 0 root root 0 Jan 1 1970 dir * $ ls -l /mnt/dir * total 0 * -rw-rw-rw- 0 root root 0 Jan 1 1970 printk * lrwxrwxrwx 0 root root 0 Jan 1 1970 symlink -> /etc/hostname * --w--w--w- 0 root root 0 Jan 1 1970 wronly * $ cat /mnt/date * --- prints the current date and time * $ cat /mnt/dir/symlink * --- prints yout hostname: symlink is a link to /etc/hostname * $ cat /mnt/dir/printk * This file can be read or written * at the end printk shows the contents * $ echo ciao > /mnt/dir/printk * --- ciao appears un umvu's console * $ echo 42 > /mnt/dir/wronly * --- on console "wronly output = 42" * $ echo ciao > /mnt/dir/wronly * --- on console "wronly accept numbers only" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unrealinfofs) struct vu_module_t vu_module = { .name = "unrealinfofs", .description = "example of informational file system" }; struct info { char *path; struct vu_stat stat; pseudo_upcall upcall; void *upcall_private; }; int upcall_date(int tag, FILE *f, int openflags, void *pseudoprivate); int upcall_printk(int tag, FILE *f, int openflags, void *pseudoprivate); int upcall_wronly(int tag, FILE *f, int openflags, void *pseudoprivate); int upcall_dir(int tag, FILE *f, int openflags, void *pseudoprivate); struct info infotree[] = { {"/", {.st_mode = S_IFDIR | 0777, .st_ino = 2}, upcall_dir, ""}, {"/date", {.st_mode = S_IFREG | 0444, .st_ino = 5}, upcall_date, NULL}, {"/dir", {.st_mode = S_IFDIR | 0777, .st_ino = 3}, upcall_dir, "/dir"}, {"/dir/symlink", {.st_mode = S_IFLNK | 0777, .st_ino = 6}, NULL, "/etc/hostname"}, {"/dir/printk", {.st_mode = S_IFREG | 0666, .st_ino = 7}, upcall_printk, NULL}, {"/dir/wronly", {.st_mode = S_IFREG | 0222, .st_ino = 8}, upcall_wronly, NULL}, {NULL, {.st_mode = 0}, NULL, NULL} }; static struct info *infofs_getinfo(const char *pathname) { struct info *scan; for (scan = infotree; scan->path != NULL; scan++) { if (strcmp(pathname, scan->path) == 0) break; } return scan; } int upcall_date(int tag, FILE *f, int openflags, void *pseudoprivate) { if (tag == PSEUDOFILE_LOAD_CONTENTS) { time_t now = time(NULL); fprintf(f,"%s",ctime(&now)); } return 0; } int upcall_printk(int tag, FILE *f, int openflags, void *pseudoprivate) { if (tag == PSEUDOFILE_LOAD_CONTENTS && (openflags & O_ACCMODE) == O_RDONLY) { fprintf(f, "This file can be read or written\nat the end printk shows the contents\n"); } if (tag == PSEUDOFILE_STORE_CLOSE && (openflags & O_ACCMODE) != O_RDONLY) { if (f != NULL) { char *line = NULL; size_t n = 0; while (getline(&line, &n, f) > 0) printk("%s",line); free(line); } } return 0; } int upcall_wronly(int tag, FILE *f, int openflags, void *pseudoprivate) { if (tag == PSEUDOFILE_STORE_CLOSE && f != NULL) { int n; if (fscanf(f, "%d", &n) == 0) printk("wronly accept numbers only\n"); else printk("wronly output = %d\n", n); } return 0; } int upcall_dir(int tag, FILE *f, int openflags, void *pseudoprivate) { char *prefix = pseudoprivate; size_t prefixlen = strlen(prefix); if (tag == PSEUDOFILE_LOAD_DIRENTS) { struct info *scan; pseudofile_filldir(f, ".", 2, DT_DIR); pseudofile_filldir(f, "..", 2, DT_DIR); for (scan = infotree; scan->path != NULL; scan++) { if (strncmp(prefix, scan->path, prefixlen) == 0 && scan->path[prefixlen] == '/' && scan->path[prefixlen + 1] != 0 && strchr(scan->path + (prefixlen + 1), '/') == NULL) pseudofile_filldir(f, scan->path + (prefixlen + 1), scan->stat.st_ino, pseudofile_mode2type(scan->stat.st_mode)); } } return 0; } static int simple_check_permission(int flags, mode_t mode) { switch (flags & O_ACCMODE) { case O_RDONLY : return (mode & S_IRUSR); case O_WRONLY : return (mode & S_IWUSR); case O_RDWR : return (mode & S_IRUSR) && (mode & S_IWUSR); } return 0; } int vu_unrealinfofs_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct info *scan = infofs_getinfo(pathname); if (scan->path != NULL) { if (simple_check_permission(flags, scan->stat.st_mode) == 0) { errno = EACCES; return -1; } if (scan->upcall != NULL) pseudofile_open(scan->upcall, scan->upcall_private, flags, fdprivate); return 0; } else { errno = ENOENT; return -1; } } int vu_unrealinfofs_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *private) { struct vu_stat *statbuf = &infofs_getinfo(pathname)->stat; if (statbuf->st_mode != 0) { *buf = *statbuf; return 0; } else { errno = ENOENT; return -1; } } #if 0 int vu_unrealinfofs_access(char *path, int mode, int flags) { return 0; } #endif ssize_t vu_unrealinfofs_readlink(char *path, char *buf, size_t bufsiz) { return pseudofile_readlink_fill(infofs_getinfo(path)->upcall_private, buf, bufsiz); } int vu_unrealinfofs_unlink(const char *pathname) { return 0; } int vu_unrealinfofs_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vu_service_t *s = vu_mod_getservice(); vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, NULL /*entry*/); errno = 0; return 0; } int vu_unrealinfofs_umount2(const char *target, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); int ret_value; if ((ret_value = vuht_del(ht, flags)) < 0) { errno = -ret_value; return -1; } return 0; } void vu_unrealinfofs_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { if (type == CHECKPATH) { } } void *vu_unrealinfofs_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, close) = pseudofile_close; vu_syscall_handler(s, read) = pseudofile_read; vu_syscall_handler(s, write) = pseudofile_write; vu_syscall_handler(s, lseek) = pseudofile_lseek; vu_syscall_handler(s, getdents64) = pseudofile_getdents64; #pragma GCC diagnostic pop return NULL; } int vu_unrealinfofs_fini(void *private) { return 0; } vuos-0.9.2/test_modules/unrealprw.c000066400000000000000000000101541476575172100174300ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* this is a test module: it is a copy of unreal supporting only pread/pwrite. when loaded the entire file system "appears" as /unreal and /unreal/unreal. $ vu_insmod unreal $ ls /etc/passwd /etc/passwd $ ls /unreal/etc/passwd /unreal/etc/passwd $ ls /unreal/unreal/etc/passwd /unreal/unreal/etc/passwd $ ls /unreal/unreal/unreal/etc/passwd /unreal/unreal/unreal/etc/passwd': No such file or directory It is possible in this way to test (some of) the correctness of vuos implementation: the operation on file or dir X must have the same result when testing the same operation on /unreal/X or /unreal/unreal/X. X -> VUOS force processes to forward the syscall requests to the kernel /unreal/X -> VUOS itself forwards the requests to the kernel /unreal/unreal/X -> VUOS forwards the requests to VUOS and then to the kernel (this latter case uses process self-virtualization) */ #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unrealprw) struct vu_module_t vu_module = { .name = "unrealprw", .description = "Mapping to FS (test pread/pwrite)", .flags = VU_USE_PRW }; int vu_unrealprw_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *private) { return syscall(__NR_getdents64, fd, dirp, count); } #if 0 int vu_unrealprw_access(char *path, int mode, int flags) { return faccessat(AT_FDCWD, path, mode, flags); } #endif static struct vuht_entry_t *ht1,*ht2; void vu_unrealprw_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { if (type == CHECKPATH) { //printk("%*.*s\n", arglen, arglen, arg); } } void *vu_unrealprw_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, lstat) = lstat; vu_syscall_handler(s, readlink) = readlink; vu_syscall_handler(s, open) = open; vu_syscall_handler(s, unlink) = unlink; vu_syscall_handler(s, mkdir) = mkdir; vu_syscall_handler(s, rmdir) = rmdir; vu_syscall_handler(s, mknod) = mknod; vu_syscall_handler(s, chmod) = chmod; vu_syscall_handler(s, lchown) = lchown; vu_syscall_handler(s, utimensat) = utimensat; vu_syscall_handler(s, symlink) = symlink; vu_syscall_handler(s, link) = link; vu_syscall_handler(s, rename) = rename; vu_syscall_handler(s, truncate) = truncate; vu_syscall_handler(s, statfs) = statfs; vu_syscall_handler(s, lgetxattr) = lgetxattr; vu_syscall_handler(s, lsetxattr) = lsetxattr; vu_syscall_handler(s, llistxattr) = llistxattr; vu_syscall_handler(s, close) = close; //vu_syscall_handler(s, read) = read; //vu_syscall_handler(s, write) = write; //vu_syscall_handler(s, lseek) = lseek; vu_syscall_handler(s, pread64) = pread; vu_syscall_handler(s, pwrite64) = pwrite; vu_syscall_handler(s, fcntl) = fcntl; #pragma GCC diagnostic pop ht1 = vuht_pathadd(CHECKPATH,"/","/unreal","unreal",0,"",s,0,NULL,NULL); ht2 = vuht_pathadd(CHECKPATH,"/","/unreal","unreal",0,"",s,0,NULL,NULL); return NULL; } int vu_unrealprw_fini(void *private) { if (ht2 && vuht_del(ht2, MNT_FORCE) == 0) ht2 = NULL; if (ht1 && vuht_del(ht1, MNT_FORCE) == 0) ht1 = NULL; return 0; } vuos-0.9.2/test_modules/unrealsock.c000066400000000000000000000045541476575172100175660ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unrealsock) struct vu_module_t vu_module = { .name = "unrealsock", .description = "tcp-ip stack server side" }; static struct vuht_entry_t *ht[3]; static int afs[3] = {AF_INET, AF_INET6, AF_NETLINK}; void *vu_unrealsock_init(void) { struct vu_service_t *s = vu_mod_getservice(); int i; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, socket) = socket; vu_syscall_handler(s, bind) = bind; vu_syscall_handler(s, connect) = connect; vu_syscall_handler(s, listen) = listen; vu_syscall_handler(s, accept4) = accept4; vu_syscall_handler(s, getsockname) = getsockname; vu_syscall_handler(s, getpeername) = getpeername; vu_syscall_handler(s, sendto) = sendto; vu_syscall_handler(s, recvfrom) = recvfrom; vu_syscall_handler(s, shutdown) = shutdown; vu_syscall_handler(s, setsockopt) = setsockopt; vu_syscall_handler(s, getsockopt) = getsockopt; vu_syscall_handler(s, epoll_ctl) = epoll_ctl; vu_syscall_handler(s, close) = close; #pragma GCC diagnostic pop for (i = 0; i < 3; i++) ht[i] = vuht_add(CHECKSOCKET, &afs[i], sizeof(int), s, 0, NULL, NULL); return NULL; } int vu_unrealsock_fini(void *private) { int i; for (i = 0; i < 3; i++) { if (ht[i] && vuht_del(ht[i], MNT_FORCE) == 0) ht[i] = NULL; } return 0; } vuos-0.9.2/test_modules/unrealuidgid.c000066400000000000000000000172531476575172100200740ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(unrealuidgid) struct vu_module_t vu_module = { .name = "unrealuidgid", .description = "virtualize uid gid" }; struct vu_uid_gid_t { pthread_rwlock_t lock; uid_t ruid, euid, suid, fsuid; gid_t rgid, egid, sgid, fsgid; int ngroups; gid_t *groups; size_t count; }; static __thread struct vu_uid_gid_t *vu_uid_gid = NULL; static void vu_uid_gid_create(void) { struct vu_uid_gid_t *new; new = malloc(sizeof(struct vu_uid_gid_t)); getresuid(&new->ruid, &new->euid, &new->suid); getresgid(&new->rgid, &new->egid, &new->sgid); new->fsuid = setfsuid(-1); new->fsgid = setfsgid(-1); new->count = 1; new->ngroups = getgroups(0, NULL); new->groups = malloc(new->ngroups * sizeof(gid_t)); if (getgroups(new->ngroups, new->groups) < 0) new->ngroups = 0; pthread_rwlock_init(&new->lock, NULL); vu_uid_gid = new; } static void vu_uid_gid_modify_lock(void) { pthread_rwlock_wrlock(&vu_uid_gid->lock); if (vu_uid_gid->count > 1) { struct vu_uid_gid_t *new; new = malloc(sizeof(struct vu_uid_gid_t)); new->ruid = vu_uid_gid->ruid; new->euid = vu_uid_gid->euid; new->suid = vu_uid_gid->suid; new->fsuid = vu_uid_gid->fsuid; new->rgid = vu_uid_gid->rgid; new->egid = vu_uid_gid->egid; new->sgid = vu_uid_gid->sgid; new->fsgid = vu_uid_gid->fsgid; new->ngroups = vu_uid_gid->ngroups; if (new->ngroups <= 0) new->groups = NULL; else { new->groups = malloc(new->ngroups * sizeof(gid_t)); memcpy(new->groups, vu_uid_gid->groups, new->ngroups * sizeof(gid_t)); } pthread_rwlock_init(&new->lock, NULL); new->count = 1; vu_uid_gid->count -= 1; pthread_rwlock_unlock(&vu_uid_gid->lock); vu_uid_gid = new; pthread_rwlock_wrlock(&vu_uid_gid->lock); } } int vu_unrealuidgid_setresfuid(uid_t ruid, uid_t euid, uid_t suid, uid_t fsuid, void *private) { if (vu_uid_gid == NULL) vu_uid_gid_create(); vu_uid_gid_modify_lock(); if (ruid != (uid_t) -1) vu_uid_gid->ruid = ruid; if (euid != (uid_t) -1) vu_uid_gid->euid = euid; if (suid != (uid_t) -1) vu_uid_gid->suid = suid; if (fsuid != (uid_t) -1) vu_uid_gid->fsuid = fsuid; pthread_rwlock_unlock(&vu_uid_gid->lock); return 0; } int vu_unrealuidgid_getresfuid(uid_t *ruid, uid_t *euid, uid_t *suid, uid_t *fsuid, void *private) { if (vu_uid_gid != NULL) { pthread_rwlock_rdlock(&vu_uid_gid->lock); if (ruid != NULL) *ruid = vu_uid_gid->ruid; if (euid != NULL) *euid = vu_uid_gid->euid; if (suid != NULL) *suid = vu_uid_gid->suid; if (fsuid != NULL) *fsuid = vu_uid_gid->fsuid; pthread_rwlock_unlock(&vu_uid_gid->lock); } else { if (fsuid != NULL) *fsuid = setfsuid(-1); getresuid(ruid, euid, suid); } return 0; } int vu_unrealuidgid_setresfgid(gid_t rgid, gid_t egid, gid_t sgid, gid_t fsgid, void *private) { if (vu_uid_gid == NULL) vu_uid_gid_create(); vu_uid_gid_modify_lock(); if (rgid != (gid_t) -1) vu_uid_gid->rgid = rgid; if (egid != (gid_t) -1) vu_uid_gid->egid = egid; if (sgid != (gid_t) -1) vu_uid_gid->sgid = sgid; if (fsgid != (gid_t) -1) vu_uid_gid->fsgid = fsgid; pthread_rwlock_unlock(&vu_uid_gid->lock); return 0; } int vu_unrealuidgid_getresfgid(gid_t *rgid, gid_t *egid, gid_t *sgid, gid_t *fsgid, void *private) { if (vu_uid_gid != NULL) { pthread_rwlock_rdlock(&vu_uid_gid->lock); if (rgid != NULL) *rgid = vu_uid_gid->rgid; if (egid != NULL) *egid = vu_uid_gid->egid; if (sgid != NULL) *sgid = vu_uid_gid->sgid; if (fsgid != NULL) *fsgid = vu_uid_gid->fsgid; pthread_rwlock_unlock(&vu_uid_gid->lock); } else { if (fsgid != NULL) *fsgid = setfsgid(-1); getresgid(rgid, egid, sgid); } return 0; } int vu_unrealuidgid_getgroups(int size, gid_t list[], void *private) { int ret_value; if (vu_uid_gid != NULL) { pthread_rwlock_rdlock(&vu_uid_gid->lock); ret_value = vu_uid_gid->ngroups; if (size != 0) { if (size < vu_uid_gid->ngroups) { ret_value = -1; errno = EINVAL; } else memcpy(list, vu_uid_gid->groups, vu_uid_gid->ngroups * sizeof(gid_t)); } pthread_rwlock_unlock(&vu_uid_gid->lock); return ret_value; } else return getgroups(size, list); } int vu_unrealuidgid_setgroups(int size, const gid_t list[], void *private) { if (size < 0) { errno = EINVAL; return -1; } if (vu_uid_gid == NULL) vu_uid_gid_create(); vu_uid_gid_modify_lock(); vu_uid_gid->ngroups = size; vu_uid_gid->groups = realloc(vu_uid_gid->groups, vu_uid_gid->ngroups * sizeof(gid_t)); memcpy(vu_uid_gid->groups, list, vu_uid_gid->ngroups * sizeof(gid_t)); return 0; } static void *vu_uid_gid_clone(void *arg) { if (vu_uid_gid != NULL) { pthread_rwlock_wrlock(&vu_uid_gid->lock); vu_uid_gid->count++; pthread_rwlock_unlock(&vu_uid_gid->lock); return vu_uid_gid; } else return NULL; } static void vu_uid_gid_exec(void *arg) { struct mod_inheritance_exec_arg *mod_exec = arg; if (mod_exec->exec_uid != (uid_t) -1) { uid_t setuid = mod_exec->exec_uid; vu_unrealuidgid_setresfuid(-1, setuid, setuid, setuid, NULL); mod_exec->exec_uid = (uid_t) -1; } if (mod_exec->exec_gid != (gid_t) -1) { gid_t setgid = mod_exec->exec_gid; vu_unrealuidgid_setresfgid(-1, setgid, setgid, setgid, NULL); mod_exec->exec_gid = (gid_t) -1; } } static void vu_uid_gid_terminate(void) { if (vu_uid_gid != NULL) { pthread_rwlock_wrlock(&vu_uid_gid->lock); vu_uid_gid->count -= 1; if (vu_uid_gid->count == 0) { struct vu_uid_gid_t *old_vu_uid_gid = vu_uid_gid; vu_uid_gid = NULL; pthread_rwlock_unlock(&old_vu_uid_gid->lock); pthread_rwlock_destroy(&old_vu_uid_gid->lock); free(old_vu_uid_gid); } else pthread_rwlock_unlock(&vu_uid_gid->lock); } } static void *vu_uid_gid_tracer_upcall(mod_inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case MOD_INH_CLONE: ret_value = vu_uid_gid_clone(arg); break; case MOD_INH_START: vu_uid_gid = ioarg; break; case MOD_INH_EXEC: vu_uid_gid_exec(arg); break; case MOD_INH_TERMINATE: vu_uid_gid_terminate(); break; } return ret_value; } static short vusc[]={ __NR_getresuid, __NR_getresgid, __NR_setresuid, __NR_setresgid, __NR_setgroups, __NR_getgroups }; #define VUSCLEN (sizeof(vusc) / sizeof(*vusc)) static struct vuht_entry_t *ht[VUSCLEN]; void *vu_unrealuidgid_init(void) { struct vu_service_t *s = vu_mod_getservice(); unsigned int i; for (i = 0; i < VUSCLEN; i++) { int vu_syscall = vu_arch_table[vusc[i]]; ht[i] = vuht_add(CHECKSC, &vu_syscall, sizeof(int), s, 0, NULL, NULL); } mod_inheritance_upcall_register(vu_uid_gid_tracer_upcall); return NULL; } int vu_unrealuidgid_fini(void *private) { unsigned int i; for (i = 0; i < VUSCLEN; i++) { if (ht[i] && vuht_del(ht[i], MNT_FORCE) == 0) ht[i] = NULL; } mod_inheritance_upcall_deregister(vu_uid_gid_tracer_upcall); return 0; } vuos-0.9.2/umvu/000077500000000000000000000000001476575172100135315ustar00rootroot00000000000000vuos-0.9.2/umvu/CMakeLists.txt000066400000000000000000000011201476575172100162630ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(UMVU_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src) set(UMVU_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB_RECURSE UMVU_SOURCES ${UMVU_SOURCES}/*.c) list(APPEND UMVU_SOURCES ${VU_DYN_SOURCES}) include_directories(${UMVU_HEADERS} ${VU_DYN_HEADER_PATH} ${VU_HEADERS}) add_executable(umvu ${UMVU_SOURCES}) set_property(TARGET umvu PROPERTY ENABLE_EXPORTS 1) target_link_libraries(umvu pthread dl) install(TARGETS umvu RUNTIME DESTINATION bin) vuos-0.9.2/umvu/include/000077500000000000000000000000001476575172100151545ustar00rootroot00000000000000vuos-0.9.2/umvu/include/arch_table.h000066400000000000000000000064661476575172100174250ustar00rootroot00000000000000#ifndef _ARCH_TABLE_H #define _ARCH_TABLE_H #include #include /* header file for the file arch_table.c which is automatically generated during the building/compilation process. The source file to generate arch_table.c is vu_syscall.conf */ #define SYSCALL_NR_OVERESTIMATION 512 /* This is the mapping between the system calls numbers as defined by the kernel for the current architecure and the correspondant vuos system call number. Several *real* system calls can be unified, processed and handled by modules as one vuos systems call. e.g. stat, lstat, fstat, fstatat, newfstatat: all are handled as __VU_lstat */ extern const uint16_t vu_arch_table[]; /* vu_arch_args defines for each system call provided by the architecure: * i) the number of arguments * ii) if the syscall has a pathname arg, and which is the index of the path arg * iii) it if is a l- symbolic link opaque call (like lchown lstat) * iv) if it is a -at system call (including a dirfd arg, e.g. openat, fstatat) * * all this information is stored in a single byte: * +-+-+-+-+-+-+-+-+ * |typ|path |nargs| * +-+-+-+-+-+-+-+-+ * * typ = 01 l-call * 02 -at call * 03 *special* cases e.g. -at calls supporting AT_SYMLINK_NOFOLLOW * path = index of the path arg (of dirfd if this is a -at call) * nargs = numebr of args. * * Writing this value in octal the first digit is typ, the second is path and the third is nargs. * vu_arch_table[__NR_write] = 03; // 3 args, no path * vu_arch_table[__NR_stat] = 012; // 2 args, the first is the path * vu_arch_table[__NR_lstat] = 0112; // 2 args, the first is the path, do not follow links * vu_arch_table[__NR_mkdirat] = 0212; // 2 args, at-type, the first is the dirfd, so the second is the path * vu_arch_table[__NR_faccessat] = 314; // 2 args, at-type, the first is dirfd, * AT_SYMLINK_NOFOLLOW is a supported flag */ extern const uint8_t vu_arch_args[]; /* virtual system calls are those provided by vuos for its services. e.g. insmod, rmmod, vuctl, msocket. umvu uses negative syscall numbers for virtual system calls */ /* vvu_arch_args provides for each *virtual* system call the same information as vu_arch_table. The elements of vvu_arch_args has the same structure of vu_arch_table. */ extern const uint8_t vvu_arch_args[]; #define ARCH_TYPE_SYMLINK_NOFOLLOW 1 #define ARCH_TYPE_IS_AT 2 #define ARCH_TYPE_IS_EXCEPTION 3 /* utility functions to read the (bit)fields of vu_arch_args and vvu_arch_args elements */ static inline int vu_arch_table_nargs(int syscall_number) { int argstag = vu_arch_args[syscall_number]; return (argstag & 0x7); } static inline int vu_arch_table_type(int syscall_number) { int argstag = vu_arch_args[syscall_number]; return argstag >> 6; } static inline int vu_arch_table_patharg(int syscall_number) { int argstag = vu_arch_args[syscall_number]; return ((argstag >> 3) & 0x7) - 1; } static inline int vvu_arch_table_nargs(int syscall_number) { int argstag = vvu_arch_args[syscall_number]; return (argstag & 0x7); } static inline int vvu_arch_table_type(int syscall_number) { int argstag = vvu_arch_args[syscall_number]; return argstag >> 6; } static inline int vvu_arch_table_patharg(int syscall_number) { int argstag = vvu_arch_args[syscall_number]; return ((argstag >> 3) & 0x7) - 1; } #endif vuos-0.9.2/umvu/include/canonicalize.h000066400000000000000000000046211476575172100177670ustar00rootroot00000000000000#ifndef CANONICALIZE_H #define CANONICALIZE_H #include #include /* canonicalize provides extended (and optimized) implementations of realpath(3) and canonicalize_file_name(3). canon_realpath can used in place of realpath, the argument named flags can configure the behavior as explained for the constants here below canon_realpath_dup provides the same extensions with respect to canonicalize_file_name. */ char *canon_realpath(const char *path, char *resolved_path, int flags, void *private); char *canon_realpath_dup(const char *path, int flags, void *private); /* if FOLLOWLINK == 0 and pathname is a symlink then it returns the realpath of the link itself instead of the realpath of the file it refers to */ #define FOLLOWLINK 1 /* if PERMIT_NONEXISTENT_LEAF == 1 the pathname may refer to a non-existent file (inside an existing directory) */ #define PERMIT_NONEXISTENT_LEAF 2 /* if PERMIT_EMPTY_PATH == 1 and pathname is an empty string it returns the cwd (or what the getcwd helper function returns). if PERMIT_EMPTY_PATH == 0 and pathname is an empty string realpath returns -1/ENOENT */ #define PERMIT_EMPTY_PATH 4 /* if CHECK_S_IXGRP_ON_DIRS == 1 canon_realpath returns -1/EACCES if S_IXGRP is not set in the return value of lmode for one of the directories in the path (unsupported) */ #define CHECK_S_IXALL_ON_DIRS 8 #define S_IXALL (S_IXUSR | S_IXGRP | S_IXOTH) /* canonicalize in virtual environments: virtual-world consistent re-definition of functions needed for canonicalize */ struct canon_ops { /* link opaque (lstat) mode_t definition of file type, 0 for non-existent file */ /* when CHECK_S_IXALL_ON_DIRS == 1, S_IXALL means search permission on directories. if CHECK_S_IXALL_ON_DIRS == 1 and at least one of S_IXALL bits is unset for lmode's return value then any further path-resolution step inside that directory is forbidden and realpath returns -1/EACCES */ mode_t (*lmode) (const char *pathname, void *private); /* same as readlink(2) */ ssize_t (*readlink) (const char *pathname, char *buf, size_t bufsiz, void *private); /* load in pathname the current pwd, return 0 upon success */ int (*getcwd) (char *pathname, size_t size, void *private); /* load in pathname the current root relative to /, return 0 upon success */ int (*getroot) (char *pathname, size_t size, void *private); }; void canon_setops(struct canon_ops *ops); #endif vuos-0.9.2/umvu/include/carrot.h000066400000000000000000000025421476575172100166220ustar00rootroot00000000000000#ifndef CARROT_H #define CARROT_H #include /* this is a helper module for hashtable. Hashtable try to match incrementally the right module/service. a match can be more specific (e.g. a subdirectory) can have a more recent epoch and may have exceptions. Checking for exceptions can be computationally expensive. It is useless if the match is then overridden by a more specific, more recent match. So, the incremental matching process stores in a "carrot" the list of matches that can have exceptions. When the incremental matching process completes, the carrot contains all the possible matches if the exceptions are confirmed or not. */ struct vuht_entry_t; struct carrot_t; /* Functions */ void carrot_free(struct carrot_t *old); /* Create the list of possible ht elements to be returned. The list is created according to the timestamp. */ struct carrot_t *carrot_insert(struct carrot_t *head, struct vuht_entry_t *elem, epoch_t time, int (*has_exception)(struct vuht_entry_t *elem)); /* Delete a specific element from the "carrot" */ struct carrot_t *carrot_delete(struct carrot_t *head, struct vuht_entry_t *elem); /* Choose the ht element to be returned, the first confirmed match is returned.*/ struct vuht_entry_t *carrot_check(struct carrot_t *head, int (*confirm)(struct vuht_entry_t *elem, void *opaque), void *opaque); #endif vuos-0.9.2/umvu/include/epoch.h000066400000000000000000000036451476575172100164330ustar00rootroot00000000000000#ifndef _EPOCH_H #define _EPOCH_H #include /* * Each node is timestamped with its starting epoch. * After each relevant operation the timestamp is updated. * * Epoch is the key concept for layered virtualization. * each system call request generated inside the hypervisor * (guardian angels, helper threads) happens at the epoch * when that virtualization was activated (e.g. the epoch * when a file system was mounted). In this way these system * calls see the world at that time, and cen be further * virtualized. * * e.g. mount a file system in /mnt at epoch 42 * mount the file system image /mnt/image on /mnt at epoch 44 * open (/mnt/myfile) at epoch 50. * /mnt/myfile is in the subtree virtualized by the mount at epoch 44. * the virtualization module (e.g. vufuse) processing the 'open' request * runs at epoch 44, so if it needs to run a system call (say * lstat("/mnt/test"...), the request is managed by the virtualization * aptivated by the "mount" at epoch 42 (at epoch 44 the second "mount" * did not exist). */ typedef uint64_t epoch_t; /* function definitions */ /* a relevant event happened. epoch is incremented by one tick */ void update_vepoch(void); /* define a new working/virtual epoch for the current thread, it returns the previous epoch (to restore the value in a second time */ epoch_t set_vepoch(epoch_t e); /* return the working/virtual epoch for the current thread, */ epoch_t get_vepoch(void); /* return the current (global) epoch, the *now* epoch */ epoch_t get_epoch(void); /* set the current (global) epoch, to the current/global/ *now* epoch */ epoch_t update_epoch(void); /* return the service_epoch if it is consistent with the current working/virtual epoch of calling thread. It returns 0 if service_epoch is too new, so this 'service'/virtualization did not exist at the current/working/virtual epoch */ epoch_t matching_epoch(epoch_t service_epoch); #endif vuos-0.9.2/umvu/include/hashtable.h000066400000000000000000000102141476575172100172560ustar00rootroot00000000000000#ifndef HASHTABLE_H #define HASHTABLE_H #include #include #include #include #include /* This hashtable data structure has a central role in umvu. * A module adds a hashtable entry to start a virtualization. * e.g. if it adds a CHECKPATH element it means that the module * virtualizes a subtree of the file system, if it adds * a CHECKSOCKET it virtualizes an address family */ struct vuht_entry_t; struct vu_service_t; #define PSEUDO_CHECK 0x80 /* Supported object types */ #define CHECKMODULE 0 // Module name #define CHECKPATH 1 // Path #define CHECKSOCKET 2 // Address Family #define CHECKCHRDEVICE 3 // chr device maj/min #define CHECKBLKDEVICE 4 // blk device #define CHECKSC 5 // Syscall # #define CHECKIOCTL 6 // ioctl request #define CHECKBINFMT 7 // Binfmt search #define CHECKFSALIAS 8 // FSAlias (just a string->string matching) #define NCHECKS 9 #define CHECKFSTYPE (PSEUDO_CHECK | CHECKMODULE) #define CHECKPATHEXACT (PSEUDO_CHECK | CHECKPATH) #define SET_EPOCH 1 #define NEGATIVE_MOUNT ((confirmfun_t)1) #define VUFLAG_PERMANENT 1 #define VUFLAG_TRAILINGNUMBERS 2 /* hashtable elements may have exception. when a confirm function is defined (as an argument adding the hashtable element) that confirm function is called prior to confirm each match */ typedef int (*confirmfun_t)(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht); /* add an element to the hashtable */ /* confirmfun is a cleanup function for CHECKMODULE */ struct vuht_entry_t *vuht_add(uint8_t type, const void *obj, int objlen, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data); /* add a path element to the hashtable: This function is similar to vuht_add * extra parameters are provided to generate teh mounttab line (see /proc/mounts) */ struct vuht_entry_t *vuht_pathadd(uint8_t type, const char *source, const char *path, const char *fstype, unsigned long mountflags, const char *mountopts, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data); /* del takes the element out from the data structure.... */ /* supported flags: MNT_FORCE MNT_DETACH (both provide immediate detach and lazy delete) */ int vuht_del(struct vuht_entry_t *hte, int umountflags); /* pick searches an entry in this hashtable. pick and drop respectively increment/decrement the usage count of the hashtable element to check if it is in use and to implemented delayed delection */ struct vuht_entry_t *vuht_pick(uint8_t type, void *arg, struct stat *st, int setepoch); /* increment the usage count of the hash table element */ void vuht_pick_again(struct vuht_entry_t *hte); /* decrement the usage count of the hash table element. * the element is deallocated when count is 0 */ void vuht_drop(struct vuht_entry_t *hte); void forall_vuht_do(uint8_t type, void (*fun)(struct vuht_entry_t *ht, void *arg), void *arg); /* write the mount table in f. It is in the format of /proc/mounts or /etc/mtab */ void vuht_get_mtab(FILE *f); /* return the object i.e. the key of hte */ const void *vuht_get_obj(struct vuht_entry_t *hte); /* modules get the relative path to the mountpoint this function converts paths to paths-for-modules (mpaths) */ const char *vuht_path2mpath(struct vuht_entry_t *hte, const char *path); /* each hashtable entry has a private data field */ void *vuht_get_private_data(struct vuht_entry_t *hte); void vuht_set_private_data(struct vuht_entry_t *hte, void *private_data); /* set the cleanup function for modules */ void vuht_set_service_cleanupfun(struct vuht_entry_t *hte, confirmfun_t cleanup_fun); /* change the epoch of a hashtable entry to the current epoch (it this useful? )*/ // void vuht_renew(struct vuht_entry_t *hte); //char *vuht_get_servicename(struct vuht_entry_t *hte); struct vu_service_t *vuht_get_service(struct vuht_entry_t *hte); unsigned long vuht_get_mountflags(struct vuht_entry_t *hte); epoch_t vuht_get_vepoch(struct vuht_entry_t *hte); int vuht_get_count(struct vuht_entry_t *hte); int vuht_get_objlen(struct vuht_entry_t *hte); void vuht_terminate(); #endif vuos-0.9.2/umvu/include/linux_32_64.h000066400000000000000000000033541476575172100173060ustar00rootroot00000000000000#ifndef LINUX_32_64_H #define LINUX_32_64_H #include /* 32/64 compatibility issues. vuos always uses the 64bits system calls. vu_* and r_vu_* refer to the 64 bit implementation */ /* blame glibc for this */ #if __WORDSIZE == 32 #define vu_stat stat64 #define vu_lstat lstat64 #define vu_fstat fstat64 #define vu_lstat lstat64 #define vu_fstat fstat64 #define vu_statfs statfs64 #define vu_fstatfs fstatfs64 #define vu_fstatat fstatat64 #define r_vu_stat r_stat64 #define r_vu_lstat r_lstat64 #define r_vu_fstat r_fstat64 #define r_vu_lstat r_lstat64 #define r_vu_fstat r_fstat64 #define r_vu_statfs r_statfs64 #define r_vu_fstatfs r_fstatfs64 #define r_vu_fstatat r_fstatat64 #else /* 64 bit architectures */ #define vu_stat stat #define vu_lstat lstat #define vu_fstat fstat #define vu_lstat lstat #define vu_fstat fstat #define vu_statfs statfs #define vu_fstatfs fstatfs #define vu_fstatat fstatat #define r_vu_stat r_stat #define r_vu_lstat r_lstat #define r_vu_fstat r_fstat #define r_vu_lstat r_lstat #define r_vu_fstat r_fstat #define r_vu_statfs r_statfs #define r_vu_fstatfs r_fstatfs #define r_vu_fstatat r_fstatat #endif /* for 32 bit hosts missing in standard include files */ struct linux_dirent { unsigned long d_ino; /* Inode number */ unsigned long d_off; /* Offset to next linux_dirent */ unsigned short d_reclen; /* Length of this linux_dirent */ char d_name[]; /* Filename (null-terminated) */ /* length is actually (d_reclen - 2 - offsetof(struct linux_dirent, d_name)) */ /* char pad; // Zero padding byte char d_type; // File type (only since Linux // 2.6.4); offset is (d_reclen - 1) */ }; void dirent64_to_dirent(void* buf, int count); #endif vuos-0.9.2/umvu/include/mountflags.h000066400000000000000000000006631476575172100175110ustar00rootroot00000000000000#ifndef MOUNTFLAGS_H #define MOUNTFLAGS_H /* translate all mount flags into options so that modules can parse options only */ /* currently used by hashtable to set up the mount line (a' la /proc/mounts) */ /* opts == NULL: return the length of the char array required else: translate mountflags as a comma separated string of options in opts */ size_t mountflags2opts(unsigned long mountflags, char *opts, size_t optslen); #endif vuos-0.9.2/umvu/include/path_utils.h000066400000000000000000000023171476575172100175040ustar00rootroot00000000000000#ifndef PATH_UTILS_H #define PATH_UTILS_H /* helper function to get a canonicalized path arguemnts from the user process. return values, if not NULL, are dynamically allocated strings, so their memory must be deallocated by free(3) */ char *get_path(int dirfd, syscall_arg_t addr, struct stat *buf, int flags, uint8_t *need_rewrite, int nested); /* get the canonicalized path of the system call described is sd. It uses arch_table to process -at calls, to decide if the system call follow synbolic links or not etc. */ char *get_syspath(struct syscall_descriptor_t *sd, struct stat *buf, uint8_t *need_rewrite); /* the same as above for nested syscalls */ char *get_nested_syspath(int syscall_number, syscall_arg_t *args, struct stat *buf, uint8_t *need_rewrite); /* the same as above for virtual syscalls */ char *get_vsyspath(struct syscall_descriptor_t *sd, struct stat *buf, uint8_t *need_rewrite); /* change the path of a system call: rewrite the path in the user process memory. The hosting kernel receives the system call request using the modified path. The new path string is stored on the stack, just below the stack pointer */ void rewrite_syspath(struct syscall_descriptor_t *sd, char *newpath); #endif vuos-0.9.2/umvu/include/ptrace_defs.h000066400000000000000000000070671476575172100176160ustar00rootroot00000000000000#ifndef __UMVIEW_PTRACE_DEFS__ #define __UMVIEW_PTRACE_DEFS__ #include #include #include #include #include /* helpers for ptrace: P_* produce an error messsage if ptrace fails, and terminates the thread P_*_NODIE produce an error messsage if ptrace fails (and continue) */ #define PTRACE(action, tracee_tid, addr, data) \ if (r_ptrace(action, tracee_tid, addr, data) == -1) { \ char errmsg[80]; \ snprintf(errmsg, 80, "%s line %d, ptrace", __FILE__, __LINE__); \ perror(errmsg); \ pthread_exit(NULL); \ } #define PTRACE_NODIE(action, tracee_tid, addr, data) \ if (r_ptrace(action, tracee_tid, addr, data) == -1) { \ char errmsg[80]; \ snprintf(errmsg, 80, "%s line %d, ptrace", __FILE__, __LINE__); \ perror(errmsg); \ } #define P_GETREGS(tracee_tid, regs) \ PTRACE(PTRACE_GETREGSET, tracee_tid, NT_PRSTATUS, &((struct iovec) {regs, sizeof(arch_regs_struct)})) #define P_SETREGS(tracee_tid, regs) \ PTRACE(PTRACE_SETREGSET, tracee_tid, NT_PRSTATUS, &((struct iovec) {regs, sizeof(arch_regs_struct)})) #define P_SYSCALL(tracee_tid, signal) PTRACE(PTRACE_SYSCALL, tracee_tid, 0L, signal) #define P_CONT(tracee_tid, signal) PTRACE(PTRACE_CONT, tracee_tid, 0L, signal) #define P_LISTEN(tracee_tid, signal) PTRACE(PTRACE_LISTEN, tracee_tid, 0L, signal) #define P_INTERRUPT(tracee_tid, signal) \ PTRACE(PTRACE_INTERRUPT, tracee_tid, 0L, signal) #define P_ATTACH(tracee_tid, signal) PTRACE(PTRACE_ATTACH, tracee_tid, 0L, signal) #define P_SEIZE(tracee_tid, signal) PTRACE(PTRACE_SEIZE, tracee_tid, 0L, signal) #define P_DETACH(tracee_tid, signal) PTRACE(PTRACE_DETACH, tracee_tid, 0L, signal) #define P_SETOPT(tracee_tid, opt) PTRACE(PTRACE_SETOPTIONS, tracee_tid, 0L, opt) #define P_GETEVENTMSG(tracee_tid, event) \ PTRACE(PTRACE_GETEVENTMSG, tracee_tid, 0L, event) #define P_GETREGS_NODIE(tracee_tid, regs) \ PTRACE_NODIE(PTRACE_GETREGSET, tracee_tid, NT_PRSTATUS, \ &((struct iovec) {regs, sizeof(arch_regs_struct)})) #define P_SETREGS_NODIE(tracee_tid, regs) \ PTRACE_NODIE(PTRACE_SETREGSET, tracee_tid, NT_PRSTATUS, \ &((struct iovec) {regs, sizeof(arch_regs_struct)})) #define P_SYSCALL_NODIE(tracee_tid, signal) \ PTRACE_NODIE(PTRACE_SYSCALL, tracee_tid, 0L, signal) #define P_CONT_NODIE(tracee_tid, signal) \ PTRACE_NODIE(PTRACE_CONT, tracee_tid, 0L, signal) #define P_DETACH_NODIE(tracee_tid, signal) \ PTRACE_NODIE(PTRACE_DETACH, tracee_tid, 0L, signal) #define P_SEIZE_NODIE(tracee_tid, signal) \ PTRACE_NODIE(PTRACE_SEIZE, tracee_tid, 0L, signal) #define PTRACE_STD_OPTS \ PTRACE_O_TRACESYSGOOD | PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | \ PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | \ PTRACE_O_TRACEEXIT | PTRACE_O_TRACESECCOMP #ifndef PTRACE_EVENT_STOP #define PTRACE_EVENT_STOP 128 #endif #endif vuos-0.9.2/umvu/include/r_table_compat.h000066400000000000000000000031711476575172100203020ustar00rootroot00000000000000/* r_xxx compat defs for arch unsupported syscalls */ #ifndef R_TABLE_H #error "do no include r_table_compat.h, use r_table.h instead" #endif #if !defined(r_fork) && defined(__NR_clone) # define r_fork() native_syscall(__NR_clone, SIGCHLD, NULL) #endif #if !defined(r_open) && defined(__NR_openat) # define r_open(...) native_syscall(__NR_openat, AT_FDCWD, ## __VA_ARGS__) #endif #if !defined(r_lstat) # if defined(__NR_fstatat) # define r_lstat(path, buf) native_syscall(__NR_fstatat, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW) # elif defined(__NR3264_fstatat) # define r_lstat(path, buf) native_syscall(__NR3264_fstatat, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW) # endif #endif #if !defined(r_readlink) && defined(__NR_readlinkat) # define r_readlink(...) native_syscall(__NR_readlinkat, AT_FDCWD, ## __VA_ARGS__) #endif #if !defined(r_unlink) && defined(__NR_unlinkat) # define r_unlink(pathname) native_syscall(__NR_unlinkat, AT_FDCWD, pathname, 0) #endif #if !defined(r_rmdir) && defined(__NR_unlinkat) # define r_rmdir(pathname) native_syscall(__NR_unlinkat, AT_FDCWD, pathname, AT_REMOVEDIR) #endif #if !defined(r_dup2) && defined(__NR_dup3) # define r_dup2(oldfd, newfd) (oldfd == newfd) ? newfd : native_syscall(__NR_dup3, oldfd, newfd, 0) #endif #if !defined(r_poll) && defined(__NR_ppoll) # define r_poll(fds, nfds, timeout) \ native_syscall(__NR_ppoll, fds, nfds, &(struct timespec){.tv_sec = timeout}, NULL) #endif #if !defined(r_epoll_wait) && defined(__NR_epoll_pwait) # define r_epoll_wait(epfd, events, maxevents, timeout) \ native_syscall(__NR_epoll_pwait, epfd, events, maxevents, timeout, NULL) #endif vuos-0.9.2/umvu/include/service.h000066400000000000000000000043421476575172100167700ustar00rootroot00000000000000#ifndef SERVICE_H #define SERVICE_H #include #include /* each module define a service. services are registered in the hashtable (he key is the module name */ struct vuht_entry_t; typedef long (*syscall_t)(); struct vu_service_t { // pointer to a static structure named "vu_module" defined in the module // this structure defines the name and a short description of the module // the presence of such a structure is used as a test (that the module // has been designed for vuos). struct vu_module_t *mod; // modules are loaded as dynamic library plug-ins. // this is the handle returned by dl_open void *dlhandle; // the hash table pointer of the service itself struct vuht_entry_t *service_ht; // private data of the module (modules can use this pointer as they please. void *private; // table of vu_syscalls implementation. syscall_t module_syscall[]; }; struct vuht_entry_t *vu_mod_getht(void); /* A static thread variable records the module hash table element chosen by the hypervisor, so that the module can access it */ void vu_mod_setht(struct vuht_entry_t *ht); /* hash table and epoch wrapper: * set the ht and epoch to run a service_syscall and then * restore the previous value. * usage: * VU_HTWRAP(ht, ret_value = service_syscall(ht, __VU_xxxx)(arg0, arg1...)); */ #define VU_HTWRAP(HT, X) \ do { \ struct vuht_entry_t *__sht = vu_mod_getht(); \ epoch_t __e = get_vepoch(); \ vu_mod_setht(HT); \ set_vepoch(vuht_get_vepoch(HT)); \ (X); \ set_vepoch(__e); \ vu_mod_setht(__sht); \ } while(0) /* inline function: it is here for performance. it returns the pointer of the syscall implementation.... an example of this inline usage is: retval = service_syscall(ht, __VU_read)(fd, buf, buflen); */ __attribute__((always_inline)) static inline syscall_t service_syscall(struct vuht_entry_t *ht, int vu_syscall_number) { struct vu_service_t *service = vuht_get_service(ht); return service->module_syscall[vu_syscall_number]; } /* inline function: it is here for performance. it returns the flags of the module */ __attribute__((always_inline)) static inline uint64_t service_getflags(struct vuht_entry_t *ht) { return ht ? vuht_get_service(ht)->mod->flags : 0; } #endif vuos-0.9.2/umvu/include/syscall_names.h000066400000000000000000000005551476575172100201670ustar00rootroot00000000000000#ifndef SYSCALL_NAMES_H #define SYSCALL_NAMES_H /* header file for the file syscall_names.c which is automatically generated during the building/compilation process. The header file is pre-processed to get the names of the the system calls provided by the kernel for the current architecture. */ const char *syscallname(int sysno); #endif vuos-0.9.2/umvu/include/syscall_table.h000066400000000000000000000030731476575172100201510ustar00rootroot00000000000000#ifndef _SYSCALL_TABLE_H_ #define _SYSCALL_TABLE_H_ /* header file for the file syscall_table.c which is automatically generated during the building/compilation process. The source file to generate arch_table.c is vu_syscall.conf */ struct syscall_descriptor_t; struct vuht_entry_t; /* a choice function processes the actual arguments of a system call request and returns the pointer to the hash table entry which is responsible to handle the request. When a choice function returns NULL it means that the request is not to be virtualized, so it is forwarded to the kernel */ typedef struct vuht_entry_t *choicef_t(struct syscall_descriptor_t *); typedef void wrapf_t(struct vuht_entry_t *, struct syscall_descriptor_t *); /* vu_syscall_table defines for each _VU_ system call: the choice function, the system call pre-processing wrapper it is evaluated before the kernel gets the system call. the system call co-processing wrapper it is evaluated while the kernel is processing the request the system call post-processing wrapper it runs when the kernel has completed the system call request */ struct syscall_tab_entry{ choicef_t *choicef; wrapf_t *wrapinf; wrapf_t *wrapduringf; wrapf_t *wrapoutf; }; struct vsyscall_tab_entry{ choicef_t *choicef; wrapf_t *wrapf; }; extern const struct syscall_tab_entry vu_syscall_table[]; extern const struct vsyscall_tab_entry vvu_syscall_table[]; /* these arrays provide the names of _VU_ system calls (for debugging purposes) */ extern const char *vu_syscall_names[]; extern const char *vvu_syscall_names[]; #endif vuos-0.9.2/umvu/include/umvu_peekpoke.h000066400000000000000000000141651476575172100202130ustar00rootroot00000000000000#ifndef UMVU_PEEKPOKE_H #define UMVU_PEEKPOKE_H /* umvu_peekpoke: exchange data with the traced/virtualized process */ #include #include #include /*Architecture dependent part*/ #if defined(__x86_64__) || defined(__i386__) #define SYSCALL_ARG_NR 6 typedef unsigned long int syscall_arg_t; #define SYSCALL_INSTRUCTION_LEN 2 typedef struct user_regs_struct arch_regs_struct; #elif defined(__riscv_xlen) && __riscv_xlen == 64 #include #define SYSCALL_ARG_NR 6 typedef unsigned long int syscall_arg_t; #define SYSCALL_INSTRUCTION_LEN 4 typedef struct user_regs_struct arch_regs_struct; #else #error Unsupported architecture #endif /* helper Macros */ #define SYSARG(type, sd, i) (type) sd->syscall_args[i] #define SET_SYSARG(sd, i, val) sd->syscall_args[i] = (syscall_arg_t)val; /* STATES of syscall processing. * A syscall request process begins in IN_SYSCALL state. * The hypervisor pre-processes the request and can decide: * to ask the kernel to skip the request (the processing completes in IN_SYSCALL state) * (this is the case of fully virtualized calls) * to ask the kernel to process the request (the processing completes in IN_SYSCALL state) * (this is the case of non virtualized calls) * to ask the kernel to process the request and call again when the system call has completed * in this case the syscall request next state is DURING_SYSCALL and then when * the kernel has completed the final state becomes OUT_SYSCALL */ typedef enum syscall_state_t { IN_SYSCALL, DURING_SYSCALL, OUT_SYSCALL } syscall_state_t; /* syscall peekpoke operations: * PEEKPOKE_ARGS: peek or poke all the syscall args * syscall# args program_counter stack_pointer * PEEKPOKE_RETVALUE: peek or poke retvalue (or error) * SKIP_SETRETVALUE: skip the syscall and set the retvalue (seccomp only) */ typedef enum peekpokeop_t { PEEK_ARGS, POKE_ARGS=PEEK_ARGS, PEEK_RETVALUE, POKE_RETVALUE=PEEK_RETVALUE, SKIP_SETRETVALUE } peekpokeop_t; #define UMVU_SKIP 0x1 #define UMVU_CB_AFTER 0x2 #define UMVU_BLOCKIT 0x4 #define UMVU_DO_IT_AGAIN 0x8 /* return codes for syscall_hadlers (mapped as bitmaps of requests enabled by that return code) e.g. BLOCKIT implies UMVU_CB_AFTER */ typedef enum syscall_action_t { DOIT = 0, SKIPIT = UMVU_SKIP, DOIT_CB_AFTER = UMVU_CB_AFTER, BLOCKIT = UMVU_BLOCKIT | UMVU_CB_AFTER, DO_IT_AGAIN = UMVU_DO_IT_AGAIN } syscall_action_t; #define ACTION_STRINGS { \ /* 0 */ "DOIT", \ /* 1 */ "SKIPIT", \ /* 2 */ "DOIT_CB_AFTER", \ /* 3 */ "?", \ /* 4 */ "?", \ /* 5 */ "?", \ /* 6 */ "BLOCKIT", \ /* 7 */ "?", \ /* 8 */ "DO_IT_AGAIN" \ /* 9 */ "?", \ /* A */ "?", \ /* B */ "?", \ /* C */ "?", \ /* D */ "?", \ /* E */ "?", \ /* F */ "?" } struct syscall_extra_t; /* system call descriptor -- all what a syscall handler need to know about a system call */ /* this structure is architecture independent */ struct syscall_descriptor_t { /* the action: default value = DOIT */ syscall_action_t action; /* orig_syscall_number is the syscall requested by the user process. syscall_number has a different value if the hypervisor requires the kernel to process a different system call */ int syscall_number; int orig_syscall_number; /* syscall arguments */ syscall_arg_t syscall_args[SYSCALL_ARG_NR]; /* the return value from the kernel */ syscall_arg_t orig_ret_value; /* the return value returned to the user process */ syscall_arg_t ret_value; /* instruction pointer/program counter of the syscall request */ uintptr_t prog_counter; /* stack pointer */ uintptr_t stack_pointer; /* pid of the watchdog process, to manage blocking calls it is 0 when the watchdog process is not active */ pid_t waiting_pid; /* pointer to further data required by the upper layers */ struct syscall_extra_t *extra; /* opaque pointer used to exchange data between the phases of syscall processing (IN_SYSCALL, DURING_SYSCALL, OUT_SYSCALL) */ void *inout; }; /* set/get the tid of the user process/thread controlled by the current hypervisor thread. (i.e. the "guarded"/"protected" thread of the current "guardian angel") */ void umvu_settid(pid_t tid); pid_t umvu_gettid(); /* block the user process: it changes the syscall to poll(0, 0, -1) */ void umvu_block(struct syscall_descriptor_t *sd); /* unblock the user process (using TRACE_INTERRUPT, poll(0, 0, -1) returns -1/EINTR */ void umvu_unblock(void); /* get the syscall info from PTRACE_GETREGS this call is architecture dependent. if op == PEEKPOKE_ARGS get syscall_number, args, prog_counter, stack_pointer otherwise (PEEKPOKE_RETVALUE) get orig_ret_value */ void umvu_peek_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op); /* store the syscall into (prepaare it for PTRACE_SETREGS) this call is architecture dependent. The return value is > 0 if PTRACE_SETREGS is required (some values changed). If teh return value is 0, PTRACE_SETREGS can be safely skipped. if op == PEEKPOKE_ARGS: store syscall_number, args, prog_counter. if op == SKIP_SETRETVALUE: store syscall_number and return value else (PEEKPOKE_RETVALUE) store just the return value. */ int umvu_poke_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op); /* in all the following functions: * the identity of the user process is implicit as these functions are for guardian angels, each angel is assigned exactly one "protected" thread. * addr is the address in the user process memory area * buf is the local buffer */ /* get a string from a user process, i.e. get data up to the first NULL byte (datalen is the max string length) */ int umvu_peek_str(uintptr_t addr, void *buf, size_t datalen); /* get a string from a user process, the max length is PATH_MAX. return a dynamic allocated copy of the string */ char *umvu_peekdup_path(uintptr_t addr); /* get exactly datalen bytes from the user process memory */ int umvu_peek_data(uintptr_t addr, void *buf, size_t datalen); /* store exactly datalen bytes from the user process memory */ int umvu_poke_data(uintptr_t addr, void *buf, size_t datalen); unsigned long umvu_get_pagesize(void); #endif vuos-0.9.2/umvu/include/umvu_tracer.h000066400000000000000000000017601476575172100176650ustar00rootroot00000000000000#ifndef UMVU_TRACER_H #define UMVU_TRACER_H #include #include /* tracer: this is the lowest layer of virtualization */ /* legacy implementation. umvu_tracer_seccomp uses seccomp (BPF) to speedup the tracing */ typedef void (*syscall_handler_t)(syscall_state_t, struct syscall_descriptor_t *); /* test if seccomp is available on the hosting system */ int umvu_tracer_test_seccomp(void); /* the tracer must be used as follows: * int wstatus; * switch(childpid = umvu_tracer_fork()) { * case 0: .... root of the virtualized processes * exit(...) * default: .... init the tracer * wstatus = umvu_tracepid(childpid, syscall_hangler, 1); * .... cleanup tracer * exit(WEXITSTATUS(wstatus)) * case -1: * .... error management */ int umvu_tracer_fork(int seccomp); int umvu_tracepid(pid_t childpid, syscall_handler_t syscall_handler_arg, int main); #endif vuos-0.9.2/umvu/include/vu_access_emu.h000066400000000000000000000005621476575172100201510ustar00rootroot00000000000000#ifndef VU_ACCESS_EMU_H #define VU_ACCESS_EMU_H #include /* this emulates 'access' given a stat buffer. use getuid (or geteuid if AT_EACCESS), getgid (or getegid if AT_EACCESS) and getgroups. return 0 or -errno AT_SYMLINK_NOFOLLOW is handled by canonicalize/path-utils */ int vu_access_emu(struct vu_stat *statbuf, int mode, int flags); #endif vuos-0.9.2/umvu/include/vu_chroot_exec.h000066400000000000000000000002231476575172100203360ustar00rootroot00000000000000#ifndef VU_CHROOT_EXEC_H #define VU_CHROOT_EXEC_H void exec_chroot_rewrite_interpreter(struct vuht_entry_t *ht, struct binfmt_req_t *req); #endif vuos-0.9.2/umvu/include/vu_execute.h000066400000000000000000000025641476575172100175100ustar00rootroot00000000000000#ifndef _VU_EXECUTE_H #define _VU_EXECUTE_H #include #include #include /* second layer of virtualization, just above the tracer. this module gets the system call requests, calls the chice functions to decide if the request must be virtualized or not, and dispatches them to the right wrappers */ #define VU_NESTED 1 #define VU_NOT_NESTED 0 /* extension of syscall state_t, field added for execute */ struct syscall_extra_t { /* the pathname of the syscall. always canonicalized. syscall using file descriptors: the canonicalized path used to open the file*/ char *path; /* module path: path relative to the mountpoint of the module */ const char *mpath; /* stat of the file: st_mode == 0 means that the file does not exist */ struct vu_stat statbuf; /* errno returned during the path resolution */ int path_errno; /* nested == 1 means self virtualization */ uint8_t nested; /* if path_rewrite == 1, the pathname will be rewritten */ uint8_t path_rewrite; /* latest syscall was a successful exec: no "out phase", cleanup needed */ uint8_t isexec; /* hash table element of the module managing this system call. NULL means not virtualized syscall */ struct vuht_entry_t *ht; /* the epoch of the module match */ epoch_t epoch; }; void vu_syscall_execute(syscall_state_t state, struct syscall_descriptor_t *sd); #endif vuos-0.9.2/umvu/include/vu_fd_table.h000066400000000000000000000032751476575172100176060ustar00rootroot00000000000000#ifndef VU_FD_TABLE_H #define VU_FD_TABLE_H #include /* This module keeps track of the file descriptors of the * corresponding user-thread (or the fd of the hypervisor itself if nested == 1). * * NB there are three layers of data structures: * fd_table (this), file_table (whose elements are named fnodes), vnode. * more elements of fd_table may point to the same fnode, several fnodes to the same vnode. * * clone/fork/exec are automatically handled: * in case of fork/clone. if CLONE_FILES the mapping is shared, copied otherwise. * in case of exec. the CLOEXEC entries are deleted. * * This module perform locking to support multithreading access */ struct vu_fnode_t; struct vuht_entry_t; /* store the mapping between fd and fnode */ void vu_fd_set_fnode(int fd, int nested, struct vu_fnode_t *fnode, int fdflags); /* delete the mapping about fd */ int vu_fd_close(int fd, int nested); /* manage a dup, copy the mapping */ void vu_fd_dup(int fd, int nested, int oldfd, int fdflags); /* helper functions to get/set specific info*/ struct vu_fnode_t *vu_fd_get_fnode(int fd, int nested); struct vuht_entry_t *vu_fd_get_ht(int fd, int nested); void vu_fd_get_path(int fd, int nested, char *dest, size_t n); mode_t vu_fd_get_mode(int fd, int nested); int vu_fd_get_fdflags(int fd, int nested); void vu_fd_set_fdflags(int fd, int nested, int flags); int vu_fd_get_flflags(int fd, int nested); void vu_fd_set_flflags(int fd, int nested, int flags); int vu_fd_get_sfd(int fd, void **pprivate, int nested); /* VU_USE_PRW */ void vu_fd_get_possize_lock(int fd, int nested, off_t *pos, off_t *size); void vu_fd_set_possize_unlock(int fd, int nested, off_t pos, off_t size); #endif vuos-0.9.2/umvu/include/vu_file_table.h000066400000000000000000000046531476575172100201350ustar00rootroot00000000000000#ifndef VU_FILE_TABLE_H #define VU_FILE_TABLE_H #include #include #include /* This if the table of open files. * * NB there are three layers of data structures: * fd_table, file_table (this, whose elements are named fnodes), vnode. * more elements of fd_table may point to the same fnode, several fnodes to the same vnode. * * dup/inherited file descriptors from fd_table point the same fnode element. * * fnodes point to the same vnode element if they refer to the same file. * * This module perform locking to support multithreading access */ struct vuht_entry_t; struct vu_fnode_t; /* set the "close" upcall. (one for each file type (see umvu/include/xstat.h) */ typedef int (* close_upcall_t)(struct vuht_entry_t *ht, int sfd, void *private); void vu_fnode_set_close_upcall(mode_t mode, close_upcall_t close_upcall); #define VU_FNODE_CLOSED ((void *) -1) /* create an f-node: * sfd is the service fd, the file descriptor used by the module to identify the file * (private == VU_FNODE_CLOSED) means that the tmp file has no corresponding * open file. e.g. tmp file for execve */ struct vu_fnode_t *vu_fnode_create( struct vuht_entry_t *ht, const char *path, struct vu_stat *stat, int flags, int sfd, void *private); /* close an fnode: delete the fnodeis the usage counter becomes 0 */ int vu_fnode_close(struct vu_fnode_t *fnode); /* increment the usage count */ void vu_fnode_dup(struct vu_fnode_t *v); /* helper functions */ struct vuht_entry_t *vu_fnode_get_ht(struct vu_fnode_t *v); void vu_fnode_get_path(struct vu_fnode_t *v, char *dest, size_t n); char *vu_fnode_get_vpath(struct vu_fnode_t *v); mode_t vu_fnode_get_mode(struct vu_fnode_t *v); int vu_fnode_get_flags(struct vu_fnode_t *v); void vu_fnode_set_flags(struct vu_fnode_t *v, int flags); int vu_fnode_get_sfd(struct vu_fnode_t *v, void **pprivate); /* a local copy of the file is required. copyinout calls the callback in 'cp' with the right arguments and opportune locking */ typedef int (*copyfun) (struct vuht_entry_t *ht, char *path, char *tmp_path); int vu_fnode_copyinout (struct vu_fnode_t *v, copyfun cp); /* VU_USE_PRW */ void vu_fnode_get_possize_lock(struct vu_fnode_t *v, off_t *pos, off_t *size); void vu_fnode_set_possize_unlock(struct vu_fnode_t *v, off_t pos, off_t size); off_t vu_fnode_getset_size(struct vuht_entry_t *ht, struct vu_stat *stat, off_t size); #endif vuos-0.9.2/umvu/include/vu_fnode_copy.h000066400000000000000000000005251476575172100201660ustar00rootroot00000000000000#ifndef VU_FNODE_COPY_H #define VU_FNODE_COPY_H struct vu_fnode_t; /* get a local copy or the filei corresponding to fnode */ int vu_fnode_copyin(struct vu_fnode_t *fnode); /* use the local copy to update the file corresponding to fnode */ int vu_fnode_copyout(struct vu_fnode_t *fnode); /* this functions use vu_fnode_copyinout */ #endif vuos-0.9.2/umvu/include/vu_fs.h000066400000000000000000000012721476575172100164510ustar00rootroot00000000000000#ifndef FS_H #define FS_H /* file system module. This module keep track of: * current working directory * chroot * umask * it automatically manages: clone/fork, termination of user-threads and hypervisor threads. * (info are shared if clone has the flag CLONE_FS set). */ /* change the working directory */ void vu_fs_set_cwd(char *wd); /* change the root directory */ void vu_fs_set_rootdir(char *dir); /* change the umask directory: return the previous mask */ mode_t vu_fs_set_umask(mode_t mask); /* helper functions */ void vu_fs_get_rootdir(char *dest, size_t n); int vu_fs_is_chroot(void); void vu_fs_get_cwd(char *dest, size_t n); mode_t vu_fs_get_umask(void); #endif vuos-0.9.2/umvu/include/vu_inheritance.h000066400000000000000000000041061476575172100203310ustar00rootroot00000000000000#ifndef VU_INHERITANCE_H #define VU_INHERITANCE_H /* inheritance management: hypervisor modules can register a callback upcall that will be called in case of the event listed in inheritance_state_t. It is used by core sections (or modules) which need to initialize and maintain thread private data structures. */ /* Warning: vu_inheritance_upcall_register is *NOT* thread safe. It has been designed for __attribute__((constructor)) functions (so the call happens before going multi-threading. A thread safe implementation for modules as an upper layer is provided by vu_mod_inheritance.c */ /* CLONE/START is the pair of events to manage a new process/thread: CLONE is an event of the parent process/creating thread START is the first event if the newborn process/thread. It is possible cor CLONE to pass data to START */ /* INH_CLONE, INH_START, INH_EXEC, INH_TERMINATE report event of user-processes INH_PTHREAD_CLONE, INH_PTHREAD_START, INH_PTHREAD_TERMINATE are about threads of the hypervisor */ typedef enum inheritance_state_t { INH_CLONE = 0, INH_START = 1, INH_EXEC = 2, INH_TERMINATE = 3, INH_PTHREAD_CLONE = 10, INH_PTHREAD_START = 11, INH_PTHREAD_TERMINATE = 13 } inheritance_state_t; typedef void *(*inheritance_upcall_t)(inheritance_state_t state, void *ioarg, void *arg); /* register an upcall handler */ void vu_inheritance_upcall_register(inheritance_upcall_t upcall); /* call all the registered handlers. vu_inheritance_call(INH_SOMETHING, NULL, commonarg): >>> all the handlers get commonarg as their arg >>> the return values of the handlers are discarded vu_inheritance_call(INH_SOMETHING, inoutarg, commonarg): >>> all the handlers get commonarg as their arg >>> each handler gets its element in inoutarg as its ioarg. >>> the return value of each handler updates its element in inoutarg. >>> inoutargs must point to a memory area vu_inheritance_inout_size() bytes wide >>> (START / CLONE events use this) */ void vu_inheritance_call(inheritance_state_t state, void **inout, void *arg); size_t vu_inheritance_inout_size(void); #endif vuos-0.9.2/umvu/include/vu_initfini.h000066400000000000000000000014771476575172100176610ustar00rootroot00000000000000#ifndef VU_INITFINI_H #define VU_INITFINI_H /* hypervisor constructors/destructors. vu_init calls all the upcalls registered by vu_constructor_register. vu_fini calls all the upcalls registered by vu_destructor_register. umvu_main calls vu_init just before starting the tracer and vu_fini as soon as the tracer terminates. Warning: these functions are *NOT* thread safe. They have been designed for __attribute__((constructor)) functions (so the call happens before going multi-threading) */ typedef void (*voidfun_t)(void); /* register a constructor/destructor */ void vu_constructor_register(voidfun_t upcall); void vu_destructor_register(voidfun_t upcall); /* this functions are for main: start all the constructors (init), and run all the destructors (fini) */ void vu_init(void); void vu_fini(void); #endif vuos-0.9.2/umvu/include/vu_log.h000066400000000000000000000132501476575172100166210ustar00rootroot00000000000000#ifndef VU_LOG_H #define VU_LOG_H /* VUOS logging facility. * */ #include #include #include #include #include #include #include #include /* printk log level encoding (inspired by the linux kernel */ #define KERN_SOH "\001" /* ASCII Start Of Header */ #define PRINTK_STANDARD_LEVEL 0 /* Prefix strings for printk encoding the message log level */ #define KERN_EMERG KERN_SOH "0" /* system is unusable */ #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ #define KERN_CRIT KERN_SOH "2" /* critical conditions */ #define KERN_ERR KERN_SOH "3" /* error conditions */ #define KERN_WARNING KERN_SOH "4" /* warning conditions */ #define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */ #define KERN_INFO KERN_SOH "6" /* informational */ #define KERN_DEBUG KERN_SOH "7" /*debug-level messages */ #define LOG_NONE -1 /* no output */ #define LOG_DEFAULT 4 /* default loglevel */ #define LOG_VDEBUG(i) 7+i /* debug-level messages */ #define MAX_DEBUG_LEVEL 10 #define STRERROR_BUF_SIZE 1024 // see NOTES in strerror_r(3) man page /* helper macros */ #define warning(cond) do { \ if (!(cond)) { \ char buf[STRERROR_BUF_SIZE]; \ printk(KERN_WARNING "warning %s:%d %s\n", basename(__FILE__), __LINE__,\ strerror_r(errno, buf, STRERROR_BUF_SIZE)); \ } \ } while(0) #define fatal(cond) do { \ if (!(cond)) { \ char buf[STRERROR_BUF_SIZE]; \ printk(KERN_WARNING "fatal %s:%d %s\n", basename(__FILE__), __LINE__,\ strerror_r(errno, buf, STRERROR_BUF_SIZE)); \ pthread_exit(NULL); \ } \ } while(0) #define warning_msg(msg) do { \ char buf[STRERROR_BUF_SIZE]; \ printk(KERN_WARNING "warning %s:%d %s\n", basename(__FILE__), __LINE__,\ strerror_r(errno, buf, STRERROR_BUF_SIZE)); \ } while(0) int vprintk(const char *fmt, va_list ap); int printk(const char *fmt, ...); void set_console_log_level(int level); void set_syslog_log_level(int level); void set_log_file(char *logfile_path); /* tagged logging. it is possible to enable/disable each log tag globally or thread by thread. active logging tag set is the union of those globally and locally aptivated. 63 tags are supported (see DEBUG_ALLTAGS) */ /* add or delete tags (tag is a string, each char is a tag to be added/deleted. */ void debug_add_tags(char *tags, int local); void debug_del_tags(char *tags, int local); /* get the map of active tags */ void debug_get_tags(char *tags, size_t size, int local); /* set the color of a tag (see vudebug(1) */ void debug_set_color(char *tags, const char *s); /* parse a color mapping string */ void debug_set_color_string(const char *s); /* debug tags may have descriptions, get/set the description */ void _debug_set_name(int index, const char *s); void debug_get_name(char tag, char *buf, size_t bufsize); #define debug_set_name(tag, s) \ _debug_set_name(DEBUG_TAG2INDEX_##tag, "" s) /* logging must be fast... if disabled. printkdebug macro calls _printkdebug if tag is active. the overall cost of the choice is reading two vars + one or operation + a conditinal branch */ extern uint64_t debugmask; extern __thread uint64_t tdebugmask; extern __thread pid_t debugtid; /* printk uses preprocessor magic to pprocess the first argument: printkdebug(x, "message %d", intarg); will be printed if tag x is active. x is converted to an integer by the macros/constants here below. The mapping has complexity O(1) */ int _printkdebug(int index, const char *fmt, ...); #define printkdebug(tag, fmt, ...) \ if (__builtin_expect((debugmask | tdebugmask) & (1ULL << DEBUG_TAG2INDEX_##tag), 0)) \ _printkdebug(DEBUG_TAG2INDEX_##tag, "%d:%d\040%s:%d " fmt "\n", \ debugtid, gettid(), basename(__FILE__), __LINE__, ##__VA_ARGS__) #define DEBUG_ALLTAGS " ABCDEFGHIJKLMNOPQRSTUVWXYZ_01234abcdefghijklmnopqrstuvwxyz56789" #define DEBUG_NTAGS sizeof(DEBUG_ALLTAGS) #define DEBUG_TAG2INDEX_ERR 0 #define DEBUG_TAG2INDEX_A 1 #define DEBUG_TAG2INDEX_B 2 #define DEBUG_TAG2INDEX_C 3 #define DEBUG_TAG2INDEX_D 4 #define DEBUG_TAG2INDEX_E 5 #define DEBUG_TAG2INDEX_F 6 #define DEBUG_TAG2INDEX_G 7 #define DEBUG_TAG2INDEX_H 8 #define DEBUG_TAG2INDEX_I 9 #define DEBUG_TAG2INDEX_J 10 #define DEBUG_TAG2INDEX_K 11 #define DEBUG_TAG2INDEX_L 12 #define DEBUG_TAG2INDEX_M 13 #define DEBUG_TAG2INDEX_N 14 #define DEBUG_TAG2INDEX_O 15 #define DEBUG_TAG2INDEX_P 16 #define DEBUG_TAG2INDEX_Q 17 #define DEBUG_TAG2INDEX_R 18 #define DEBUG_TAG2INDEX_S 19 #define DEBUG_TAG2INDEX_T 20 #define DEBUG_TAG2INDEX_U 21 #define DEBUG_TAG2INDEX_V 22 #define DEBUG_TAG2INDEX_W 23 #define DEBUG_TAG2INDEX_X 24 #define DEBUG_TAG2INDEX_Y 25 #define DEBUG_TAG2INDEX_Z 26 #define DEBUG_TAG2INDEX__ 27 #define DEBUG_TAG2INDEX_0 28 #define DEBUG_TAG2INDEX_1 29 #define DEBUG_TAG2INDEX_2 30 #define DEBUG_TAG2INDEX_3 31 #define DEBUG_TAG2INDEX_4 32 #define DEBUG_TAG2INDEX_a 33 #define DEBUG_TAG2INDEX_b 34 #define DEBUG_TAG2INDEX_c 35 #define DEBUG_TAG2INDEX_d 36 #define DEBUG_TAG2INDEX_e 37 #define DEBUG_TAG2INDEX_f 38 #define DEBUG_TAG2INDEX_g 39 #define DEBUG_TAG2INDEX_h 40 #define DEBUG_TAG2INDEX_i 41 #define DEBUG_TAG2INDEX_j 42 #define DEBUG_TAG2INDEX_k 43 #define DEBUG_TAG2INDEX_l 44 #define DEBUG_TAG2INDEX_m 45 #define DEBUG_TAG2INDEX_n 46 #define DEBUG_TAG2INDEX_o 47 #define DEBUG_TAG2INDEX_p 48 #define DEBUG_TAG2INDEX_q 49 #define DEBUG_TAG2INDEX_r 50 #define DEBUG_TAG2INDEX_s 51 #define DEBUG_TAG2INDEX_t 52 #define DEBUG_TAG2INDEX_u 53 #define DEBUG_TAG2INDEX_v 54 #define DEBUG_TAG2INDEX_w 55 #define DEBUG_TAG2INDEX_x 56 #define DEBUG_TAG2INDEX_y 57 #define DEBUG_TAG2INDEX_z 58 #define DEBUG_TAG2INDEX_5 59 #define DEBUG_TAG2INDEX_6 60 #define DEBUG_TAG2INDEX_7 61 #define DEBUG_TAG2INDEX_8 62 #define DEBUG_TAG2INDEX_9 63 #endif vuos-0.9.2/umvu/include/vu_mmap_table.h000066400000000000000000000007361476575172100201460ustar00rootroot00000000000000#ifndef VU_MMAP_TABLE_H #define VU_MMAP_TABLE_H #include /* keep track of mmapped regions. CLONE/EXEC/TERMINATE are processed automatically. The fnode is kept alive as long as there are mapped regions */ struct fnode; void vu_mmap_mmap(uintptr_t addr, size_t length, struct vu_fnode_t *fnode, off_t offset); void vu_mmap_munmap(uintptr_t addr, size_t length); void vu_mmap_mremap(uintptr_t addr, size_t length, uintptr_t newaddr, size_t newlength); #endif vuos-0.9.2/umvu/include/vu_mod_inheritance.h000066400000000000000000000005661476575172100211760ustar00rootroot00000000000000#ifndef VU_MOD_INHERITANCE_H #define VU_MOD_INHERITANCE_H #include /* it manages inheritance for modules */ /* the interface to modules is in include/vumodule.h */ /* vu_exec_setuid, vu_exec_setgid are used by vu_wrap_exec to request setuid/setgid to uid/gid virtualization modules */ void vu_exec_setuid(uid_t uid); void vu_exec_setgid(gid_t gid); #endif vuos-0.9.2/umvu/include/vu_modutils.h000066400000000000000000000011531476575172100176770ustar00rootroot00000000000000#ifndef VU_MODUTILS_H #define VU_MODUTILS_H /* loading/unloading of modules */ /* vu_syscall_handler_pointer is used in vumodule.h by "vu_syscall_handler" macro */ typedef void (* voidfun)(void); struct vu_service_t; typedef long (*syscall_t)(); struct vu_service_t *module_load(const char *modname); syscall_t *vu_syscall_handler_pointer(struct vu_service_t *service, char *name); void module_unload(struct vu_service_t *service); voidfun *module_getsym(struct vu_service_t *service, char *symbol); void module_run_init(struct vu_service_t *service); int module_run_fini(struct vu_service_t *service); #endif vuos-0.9.2/umvu/include/vu_name.h000066400000000000000000000002321476575172100167540ustar00rootroot00000000000000#ifndef VU_NAME_H #define VU_NAME_H /* manage the hypervisor's name */ void set_vu_name(char *name); void get_vu_name(char *name, size_t len); #endif vuos-0.9.2/umvu/include/vu_nesting.h000066400000000000000000000014141476575172100175060ustar00rootroot00000000000000#ifndef VU_NESTING_H #define VU_NESTING_H /* enable and disable nested virtualization */ void vu_nesting_enable(void); void vu_nesting_disable(void); /* vu_nesting_init enables the self virtualization using libpurelibc. vuos supports nested virtualization using self virtualization. This function execs and then restarts the entire hypervisor to enable LD_PRELOAD of libpurelibc */ /* If libpurelibc.so is not preloaded, this function add it to LD_PRELOAD and umvu re-execute itself. The nested virtualization is enabled during the "second execution". In this way purelibc is loaded at the top of the library hierarchy allowing its functions implementation to prevail over the other libraries functions.*/ void vu_nesting_init(int argc, char **argv); #endif vuos-0.9.2/umvu/include/vu_pushpop.h000066400000000000000000000012071476575172100175350ustar00rootroot00000000000000#ifndef VU_PUSHPOP_H #define VU_PUSHPOP_H #include /* push and pop data on the user process stack. */ /* The area above the stack pointer of the process is used to store data (pathnames, memory structures) nededed by the kernel to run the system call. This method does not generate conflicts because the kernel uses a different stack area to process the system call and when the syscall returns, the values stored above the SP aren't used anymore. */ syscall_arg_t vu_push(struct syscall_descriptor_t *sd, void *buf, size_t datalen); void vu_pop(struct syscall_descriptor_t *sd, void *buf, size_t datalen); #endif vuos-0.9.2/umvu/include/vu_slow_calls.h000066400000000000000000000015121476575172100202000ustar00rootroot00000000000000#ifndef VU_SLOW_CALLS_H #define VU_SLOW_CALLS_H /* management of slow syscalls: those who may block. */ #include struct vuht_entry_t; struct slowcall; /* This syscall requires 'events' to run, use this in the IN wrapper */ struct slowcall *vu_slowcall_in(struct vuht_entry_t *ht, int fd, uint32_t events, int nested); /* suspend until the slowcall can succeed. the return value must be assigned to sd->waiting_pid */ /* this function is for the DURING wrapper */ pid_t vu_slowcall_during(struct slowcall *sc); /* this function is forthe OUT wrapper. undo what vu_slowcall_in did and free the struct slowcall */ void vu_slowcall_out(struct slowcall *sc, struct vuht_entry_t *ht, int fd, uint32_t events, int nested); /* test if the syscall can run (it is an optimization)*/ int vu_slowcall_test(struct slowcall *sc); #endif vuos-0.9.2/umvu/include/vu_thread_sd.h000066400000000000000000000006411476575172100177750ustar00rootroot00000000000000#ifndef _VU_THREAD_SD_H #define _VU_THREAD_SD_H /* get/set the syscall_descriptor of the current thread, mainly for modules */ /* set the new value of thread_sd and return the previous, so that it can be used later to restore the old value */ struct syscall_descriptor_t *set_thread_sd(struct syscall_descriptor_t *sd); /* get the current thread_sd */ struct syscall_descriptor_t *get_thread_sd(void); #endif vuos-0.9.2/umvu/include/vu_tmpdir.h000066400000000000000000000003221476575172100173330ustar00rootroot00000000000000#ifndef VU_TMPDIR_h #define VU_TMPDIR_h /* Each umvu instance uses a hidden directory in /tmp to store temporary files. This function returns the path of that directory */ char *vu_tmpdirpath(void); #endif vuos-0.9.2/umvu/include/vu_uidgid.h000066400000000000000000000010241476575172100173010ustar00rootroot00000000000000#ifndef VU_UIDGID_H #define VU_UIDGID_H #include void vu_uidgid_getresfuid(uid_t *ruid, uid_t *euid, uid_t *suid, uid_t *fsuid); void vu_uidgid_setresfuid(const uid_t ruid, const uid_t euid, const uid_t suid, const uid_t fsuid); void vu_uidgid_getresfgid(gid_t *rgid, gid_t *egid, gid_t *sgid, gid_t *fsgid); void vu_uidgid_setresfgid(const gid_t rgid, const gid_t egid, const gid_t sgid, const gid_t fsgid); int vu_uidgid_getgroups(int size, gid_t list[]); int vu_uidgid_setgroups(int size, gid_t list[]); #endif vuos-0.9.2/umvu/include/vu_vnode.h000066400000000000000000000034371476575172100171610ustar00rootroot00000000000000#ifndef VU_VNODE_H #define VU_VNODE_H /* This if the table of vnodes. * * NB there are three layers of data structures: * fd_table, file_table (whose elements are named fnodes), vnode (this). * more elements of fd_table may point to the same fnode, several fnodes to the same vnode. * * dup/inherited file descriptors from fd_table point the same fnode element. * fnodes point to the same vnode element if they refer to the same file. * * This module perform locking to support multithreading access */ struct vu_node_t; struct vuht_entry_t; /* open/close a vnode element ht+dev+inode together are the unique identifier of a vnode, open creates a new element if that file has not been opened yet, otherwise it returns the pointer to the corresponding vnode (and increment the usage counter) */ struct vu_vnode_t *vu_vnode_open(struct vuht_entry_t *ht, ino_t dev, ino_t inode, off_t size, int trunc); /* close decrements the usage counter, delete the local copy and free the vnode when the counter becomes zero) */ void vu_vnode_close(struct vu_vnode_t *vnode); /* this is the pathname of a real file, local "image" of a virtual file. it is often an empty file used to open "something" in the user process to allocate a file descriptor. The original contents of the file is loaded to support mmap or execve */ char *vu_vnode_getvpath(struct vu_vnode_t *vnode); typedef int (*copyfun) (struct vuht_entry_t *ht, char *path, char *tmp_path); int vu_vnode_copyinout (struct vu_vnode_t *vnode, char *path, copyfun cp); /* VU_USE_PRW: get and set size + locking */ off_t vu_vnode_get_size_lock(struct vu_vnode_t *vnode); void vu_vnode_set_size_unlock(struct vu_vnode_t *vnode, off_t size); off_t vu_vnode_getset_size(struct vuht_entry_t *ht, ino_t dev, ino_t inode, off_t size); #endif vuos-0.9.2/umvu/include/vu_wrap_rw_multiplex.h000066400000000000000000000013331476575172100216230ustar00rootroot00000000000000#ifndef VU_WRAP_RW_MULTIPLEX_H #define VU_WRAP_RW_MULTIPLEX_H #include #include /* this hypervisor module defines the wrappers for read and write and dispatch the requests to specific wrappersdepending on the file types. e.g. read and write have different implementations when operating on regular files, block or char devices. read/write are mapped to recvmsg/sendmsg if the file is a socket. The following functions permits the definition of specific handlers per file type */ void multiplex_read_wrappers(mode_t mode, wrapf_t wrapin, wrapf_t wrapduring, wrapf_t wrapout); void multiplex_write_wrappers(mode_t mode, wrapf_t wrapin, wrapf_t wrapduring, wrapf_t wrapout); #endif vuos-0.9.2/umvu/include/vu_wrapper_utils.h000066400000000000000000000107051476575172100207420ustar00rootroot00000000000000#ifndef VU_WRAPPER_UTILS_H #define VU_WRAPPER_UTILS_H #include #include #include /* macro for wrappers. * stucture for read wrappers/large buffers: * vu_alloc_arg(addr, var, size, nested) * vu_poke_arg(addr, var, size, nested) * vu_free_arg(var, nested) * stucture for write wrappers/large buffers: * vu_alloc_peek_arg(addr, var, size, nested) * vu_free_arg(var, nested) * stucture for read wrappers/small buffers (stack allocation) * vu_alloc_local_arg(addr, var, size, nested) * vu_poke_arg(addr, var, size, nested) * stucture for write wrappers/small buffers (stack allocation) * vu_alloc_peek_local_arg(addr, var, size, nested) * stucture for read wrappers IOVEC * vu_alloc_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested) * it assigns buf and bufsize... * vu_poke_iov_arg(iovaddr, iov, iovcnt, buf, len, nested) * vu_free_iov_arg(iov, buf, nested) * stucture for write wrappers IOVEC * vu_alloc_peek_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested) * vu_free_iov_arg(iov, buf, nested) * warning: local_arg macros define a local var. */ /* the callers of nested system calls run in the memory space of the * hypervisor, thus pointers can be simply used as such, there is no need to * allocate memory or copy data */ #define vu_alloc_arg(addr, var, size, nested) \ do { \ if (nested) { \ var = (typeof(var)) addr; \ } else { \ var = malloc(size); \ } \ } while(0) #define vu_alloc_peek_arg(addr, var, size, nested) \ do { \ if (nested) { \ var = (typeof(var)) addr; \ } else { \ var = malloc(size); \ umvu_peek_data(addr, var, size); \ } \ } while(0) #if 0 #define vu_peek_local_arg(addr, var, count, nested) \ typeof(* var) __ ## var[(nested) ? 0 : count]; \ var = (nested) ? (typeof(var)) addr : __ ## var #endif #define vu_alloc_local_arg(addr, var, size, nested) \ char *__ ## var[(nested) ? 0 : size]; \ var = (nested) ? (typeof(var)) addr : (typeof(var)) __ ## var #define vu_alloc_peek_local_arg(addr, var, size, nested) \ char *__ ## var[(nested) ? 0 : size]; \ do { \ if (nested) {\ var = (typeof(var)) addr; \ } else { \ var = (typeof(var)) __ ## var ;\ umvu_peek_data(addr, var, size); \ } \ } while(0) #define vu_alloc_peek_local_strarg(addr, var, size, nested) \ char *__ ## var[(nested) ? 0 : size]; \ do { \ if (nested) {\ var = (typeof(var)) addr; \ } else { \ var = (typeof(var)) __ ## var ;\ umvu_peek_str(addr, var, size); \ } \ } while(0) #define vu_poke_arg(addr, var, size, nested) \ do { \ if (!nested) umvu_poke_data(addr, var, size); \ } while(0) #define vu_peek_arg(addr, var, size, nested) \ do { \ if (!nested) umvu_peek_data(addr, var, size); \ } while(0) #define vu_free_arg(var, nested) \ do { \ if (!nested) xfree(var); \ } while(0) __attribute__((always_inline)) static inline size_t iovec_bufsize(struct iovec *iov, int iovcnt) { int i; size_t ret_value = 0; for (i = 0; i < iovcnt; i++) ret_value += iov[i].iov_len; return ret_value; } __attribute__((always_inline)) static inline void vu_peek_iov_arg(uintptr_t iovaddr, struct iovec *iov, int iovcnt, void *buf, int nested) { char *cbuf = (char *) buf; int i; for (i = 0; i < iovcnt; i++) { ssize_t len = iov[i].iov_len; vu_peek_arg((uintptr_t) iov[i].iov_base, cbuf, len, nested); cbuf += len; } } __attribute__((always_inline)) static inline void vu_poke_iov_arg(uintptr_t iovaddr, struct iovec *iov, int iovcnt, void *buf, size_t len, int nested) { int i; char *cbuf = (char *) buf; for (i = 0; i < iovcnt && len > 0; i++) { size_t iov_len = iov[i].iov_len; if (len < iov_len) iov_len = len; vu_poke_arg((uintptr_t) iov[i].iov_base, cbuf, iov_len, nested); len -= iov_len; cbuf += iov_len; } } #define vu_alloc_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested) \ do { \ vu_alloc_peek_arg(iovaddr, iov, sizeof(struct iovec) * iovcnt, nested); \ bufsize = iovec_bufsize(iov, iovcnt); \ buf = malloc(bufsize); \ } while(0) #define vu_alloc_peek_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested) \ do { \ vu_alloc_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested); \ vu_peek_iov_arg(iovaddr, iov, iovcnt, buf, nested); \ } while(0) #define vu_free_iov_arg(iov, buf, nested) \ do {\ vu_free_arg(iov, nested); \ xfree(buf); \ } while(0) #define default_nosys(sd) \ do {\ (sd)->action = SKIPIT; \ (sd)->ret_value = -ENOSYS; \ return; \ } while(0) #endif vuos-0.9.2/umvu/include/xcommon.h000066400000000000000000000003401476575172100170020ustar00rootroot00000000000000#ifndef XCOMMON_H #define XCOMMON_H /* xfree and xstrdup does not break if the argument is NULL */ #define xfree(ptr) do { \ if (ptr) \ free(ptr); \ } while(0) #define xstrdup(ptr) \ (ptr ? strdup(ptr) : NULL) #endif vuos-0.9.2/umvu/include/xstat.h000066400000000000000000000026531476575172100164760ustar00rootroot00000000000000#ifndef VU_XSTAT_H #define VU_XSTAT_H #include /* define arrays having one element per file type */ #define S_MODE2TYPE(X) (((X) >> 12) & 0xf) #define S_TYPES 16 #define S_TYPES_INIT(X) (X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X),(X) /* Table of file types: * 0 0000000 the file does not exist * 1 0010000 S_IFIFO * 2 0020000 S_IFCHR * 3 0030000 not assigned * 4 0040000 S_IFDIR * 5 0050000 not assigned * 6 0060000 S_IFBLK * 7 0070000 not assigned * 8 0100000 S_IFREG * 9 0110000 S_IFSIGNALFD * 10 0120000 S_IFLNK * 11 0130000 S_IFEVENTFD * 13 0150000 S_IFTIMERFD * 12 0140000 S_IFSOCK * 14 0160000 S_IFSTACK * 15 0170000 S_IFEPOLL */ /* define file types not currently handled in inodes */ #define S_IFEPOLL __S_IFEPOLL #define S_IFSTACK __S_IFSTACK #define S_IFSIGNALFD __S_IFSIGNALFD #define S_IFTIMERFD __S_IFTIMERFD #define S_IFEVENTFD __S_IFEVENTFD #define __S_IFEPOLL 0170000 /* Epoll */ #define __S_IFSTACK 0160000 /* Stack */ #define __S_IFSIGNALFD 0110000 /* Signalfd */ #define __S_IFTIMERFD 0150000 /* Timerfd */ #define __S_IFEVENTFD 0130000 /* Eventfd */ #define S_ISEPOLL __S_ISTYPE((mode), __S_IFEPOLL) #define S_ISSTACK __S_ISTYPE((mode), __S_IFSTACK) #define S_ISESIGNALFD __S_ISTYPE((mode), __S_IFESIGNALFD) #define S_ISETIMERFD __S_ISTYPE((mode), __S_IFETIMERFD) #define S_ISEEVENTFD __S_ISTYPE((mode), __S_IFEEVENTFD) #endif vuos-0.9.2/umvu/src/000077500000000000000000000000001476575172100143205ustar00rootroot00000000000000vuos-0.9.2/umvu/src/canonicalize.c000066400000000000000000000256421476575172100171340ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include "canonicalize.h" #define DOTDOT 1 #define ROOT 2 /* canonstruct: this struct contains the values that must be shared during the whole recursive scan: .ebuf: source for the relative to absolute path translation. it is allocated on the stack. if the path to translate begins by '/': it contains the root dir followed by the path to translate, otherwise it contains the current working dir followed by the path to translate .start, .end: pointers on ebuf, the boundaries of the current component .resolved: the user provided buffer where the result must be stored .rootlen: the len of the root component (it is not possible to generate shorter pathnames to force the root cage. rootlen includes the '/') .num_links: counter of symlink to avoid infinite loops (ELOOP) .mode: lstat's st_mode of the last component (of the file at the end) 0 means non-existent -1 invalid (lstat must be called again). .flags: flags (see canonicalize.h), follow link (final component) this flag is for l-system calls like lstat, lchmod, lchown etc... permit nonexistent leaves, etc. .private: opaque arg for user provided access functions (lmode, readlink, getcwd, getroot). */ struct canonstruct { char ebuf[PATH_MAX]; char *start; char *end; char *resolved; void *private; mode_t mode; short rootlen; short num_links; int flags; }; static mode_t default_lmode(const char *pathname, void *private); static ssize_t default_readlink(const char *pathname, char *buf, size_t bufsiz, void *private); static int default_getcwd(char *pathname, size_t size, void *private); static int default_getroot(char *pathname, size_t size, void *private); /* default access functions */ static struct canon_ops operations = { .lmode = default_lmode, .readlink = default_readlink, .getcwd = default_getcwd, .getroot = default_getroot, }; static mode_t default_lmode(const char *pathname, void *private) { struct stat buf[1]; //printf("LMODE %s\n", pathname); if (lstat(pathname, buf) == 0) return buf->st_mode; else return 0; } static ssize_t default_readlink(const char *pathname, char *buf, size_t bufsiz, void *private) { return readlink(pathname, buf, bufsiz); } static int default_getcwd(char *pathname, size_t size, void *private) { return getcwd(pathname, size) ? 0 : -1; } static int default_getroot(char *pathname, size_t size, void *private) { strcpy(pathname, "/"); return 0; } /* recursive generation of the canonicalized absolute path */ static int rec_realpath(struct canonstruct *cdata, char *dest) { char *newdest; /* LOOP (***) This loop manages '.' '..' (DOTDOT) from an inner call ROOT if this is the root dir layer */ while (1) { int lastlen; *dest = 0; /* delete multiple slashes / */ while (*cdata->start == '/') cdata->start++; /* find the next component */ for (cdata->end = cdata->start; *cdata->end && *cdata->end != '/'; ++cdata->end) ; lastlen = cdata->end - cdata->start; /* '.': continue with the next component of the path, forget this */ if (lastlen == 1 && cdata->start[0] == '.') { cdata->start=cdata->end; continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ } if (lastlen == 2 && cdata->start[0] == '.' && cdata->start[1] == '.') { cdata->start=cdata->end; /* return DOTDOT only if this does not go outside the current root */ if (dest > cdata->resolved+cdata->rootlen) return DOTDOT; else continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ } /* nothing more to do */ if (lastlen == 0) { if (cdata->mode == (unsigned) -1) cdata->mode = operations.lmode(cdata->resolved, cdata->private); errno = 0; return 0; } /* overflow check */ if (dest + lastlen > cdata->resolved + PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* add the new component */ newdest=dest; if (newdest[-1] != '/') *newdest++='/'; newdest=mempcpy(newdest,cdata->start,lastlen); *newdest=0; /* does the file exist? */ if ((cdata->mode = operations.lmode(cdata->resolved, cdata->private)) == 0) { if ((cdata->flags & PERMIT_NONEXISTENT_LEAF) && errno == ENOENT && *cdata->end != '/') { errno = 0; return 0; } else /* forward the errno returned by lmode. */ return -1; } /* Symlink case */ if (S_ISLNK(cdata->mode) && ((*cdata->end == '/') || (cdata->flags & FOLLOWLINK))) { /* root dir must be already canonicalized. symlinks navigating inside the root link are errors */ if (dest < cdata->resolved+cdata->rootlen) { errno = ENOENT; return -1; } else { char buf[PATH_MAX]; int len,n; /* test for symlink loops */ if (++cdata->num_links > MAXSYMLINKS) { errno = ELOOP; return -1; } /* read the link */ n = operations.readlink(cdata->resolved, buf, PATH_MAX-1, cdata->private); if (n < 0) { return -1; } buf[n]=0; /* overflow check */ len=strlen(cdata->end); if (n+len >= PATH_MAX) { errno = ENAMETOOLONG; return -1; } /* append symlink and remaining part of the path, the latter part is moved inside ebuf itself */ cdata->mode = -1; memmove(cdata->ebuf+n,cdata->end,len+1); cdata->end = memcpy(cdata->ebuf,buf,n); /* note that ebuf contains only the concatenation of the link target and the reamining part of the original path. The heading part of the source path can be lost but this is not a problem as the recursion can invalidate part of the destination string; in no cases there is the need to read some of the previous components of the source path */ /* if the symlink is absolute the scan must return back to the current root otherwise from the same dir of the symlink */ if (*buf == '/') { cdata->start=cdata->ebuf; /* if there is an absolute link in the main dir, do not return ROOT (as this is the first invocation of the recursive function), but just start the loop from the beginning */ if (dest > cdata->resolved+1) return ROOT; else continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ } else { cdata->start=cdata->end; continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ } } } /* consistency checks on dirs: all the components of the path but the last one must be directories and must have 'x' permission */ if (*cdata->end == '/') { if (!S_ISDIR(cdata->mode)) { errno = ENOTDIR; return -1; } /* check S_IXALL if requested */ else if ((cdata->flags & CHECK_S_IXALL_ON_DIRS) && (cdata->mode & S_IXALL) != S_IXALL) { errno = EACCES; return -1; } } /* okay: recursive call for the next component */ cdata->start=cdata->end; switch(rec_realpath(cdata,newdest)) { /* success. close recursion */ case 0 : return 0; /* DOTDOT: cycle at this layer */ case DOTDOT: cdata->mode = -1; continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ /* ROOT: close recursive calls up the root */ case ROOT: cdata->mode = -1; if (dest > cdata->resolved+cdata->rootlen) return ROOT; else continue; /* CONTINUE: NEXT ITERATION OF THE LOOP (***) */ /* Error */ default: return -1; } } } /* absolute path: copy prefix in cdata->ebuf return the length of the prefix (not including trailing '/' as the path has a leading '/' already) */ static ssize_t abs_prefix(struct canonstruct *cdata) { char *root = cdata->ebuf; size_t prefixlen; operations.getroot(root, PATH_MAX, cdata->private); prefixlen = strlen(root); if (root[prefixlen - 1] == '/') prefixlen--; memcpy(cdata->ebuf, root, prefixlen); cdata->rootlen = prefixlen + 1; return prefixlen; } /* relative path: copy cwd in cdata->ebuf as a prefix return the length of the prefix (including a trailing '/' to catenate the path as it is)*/ static ssize_t rel_prefix(struct canonstruct *cdata) { char root[PATH_MAX]; size_t rootlen; char *cwd = cdata->ebuf; size_t cwdlen; if (operations.getroot(root, PATH_MAX, cdata->private) < 0) return -1; rootlen = strlen(root); if (root[rootlen - 1] != '/') root[rootlen++] = '/'; if (operations.getcwd(cwd, PATH_MAX, cdata->private) < 0) return -1; cwdlen = strlen(cwd); if (cwd[cwdlen-1] != '/') cwd[cwdlen++]='/'; if (strncmp(cdata->ebuf,root,rootlen)==0) cdata->rootlen = rootlen; else cdata->rootlen = 1; return cwdlen; } /* realpath: path: path to be canonicalized, resolved_path: a buffer of PATH_MAX chars for the result return resolved or NULL on failures. errno is set consistently */ char *canon_realpath(const char *path, char *resolved_path, int flags, void *private) { struct canonstruct cdata = { .resolved = resolved_path, .private = private, .flags = flags, .mode = operations.lmode("/", private), .num_links = 0}; size_t pathlen; ssize_t prefixlen; /* arg consistency check */ if (__builtin_expect(path == NULL, 0)) { errno = EINVAL; return NULL; } if (__builtin_expect(*path == 0, 0)) { if (flags & PERMIT_EMPTY_PATH && operations.getcwd(resolved_path, PATH_MAX, private) >= 0) return resolved_path; else { errno = ENOENT; return NULL; } } pathlen = strlen(path); prefixlen = (*path == '/') ? abs_prefix(&cdata) : rel_prefix(&cdata); if (prefixlen < 0) return NULL; if (prefixlen + pathlen + 1 > PATH_MAX) { errno = ENAMETOOLONG; return NULL; } memcpy(cdata.ebuf + prefixlen, path, pathlen + 1); /* printf("PATH! %s (root=%*.*s file=%s)\n",cdata.ebuf,cdata.rootlen,cdata.rootlen,cdata.ebuf,cdata.ebuf+cdata.rootlen); */ resolved_path[0]='/'; cdata.start=cdata.ebuf+1; /* start the recursive canonicalization function */ if (rec_realpath(&cdata,resolved_path+1) < 0) { *resolved_path=0; return NULL; } else { return resolved_path; } } char *canon_realpath_dup(const char *path, int flags, void *private) { char resolved_path[PATH_MAX]; char *realpath = canon_realpath(path, resolved_path, flags, private); return realpath != NULL ? strdup(resolved_path) : NULL; } void canon_setops(struct canon_ops *ops) { operations = *ops; } vuos-0.9.2/umvu/src/carrot.c000066400000000000000000000077721476575172100157730ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include /* CARROT_FREE_LIST: recycle previously allocated struct carrot_t elements. */ #define CARROT_FREE_LIST struct carrot_t { struct vuht_entry_t *elem; epoch_t time; struct carrot_t *next; }; #ifdef CARROT_FREE_LIST pthread_mutex_t carrot_free_mutex = PTHREAD_MUTEX_INITIALIZER; static struct carrot_t *carrot_free_head = NULL; static struct carrot_t *carrot_alloc() { struct carrot_t *retvalue = NULL; pthread_mutex_lock(&carrot_free_mutex); if (carrot_free_head == NULL) { pthread_mutex_unlock(&carrot_free_mutex); retvalue = malloc(sizeof(struct carrot_t)); fatal(retvalue); } else { retvalue = carrot_free_head; carrot_free_head = carrot_free_head->next; pthread_mutex_unlock(&carrot_free_mutex); } return retvalue; } void carrot_free(struct carrot_t *old) { if (old != NULL) { struct carrot_t *scan; pthread_mutex_lock(&carrot_free_mutex); for (scan = old; scan->next != NULL; scan = scan->next) ; scan->next = carrot_free_head; carrot_free_head = old; pthread_mutex_unlock(&carrot_free_mutex); } } #else static struct carrot_t *carrot_alloc() { struct carrot_t *rv = NULL; rv = malloc(sizeof(struct carrot_t)); fatal(rv); return rv; } void carrot_free(struct carrot_t *old) { while (old) { struct carrot_t *next; next = old->next; free(old); old = next; } } #endif /* insert an element in the carrot. * If the element is "newer": * if there are exception: add an element as first element of the carrot. * otherwise: then new carrot has only one element (eventual existing carrot is deleted */ struct carrot_t *carrot_insert(struct carrot_t *head, struct vuht_entry_t *elem, epoch_t time, int (*has_exception)(struct vuht_entry_t *)) { if (head == NULL || /* empty carrot */ time > head->time) { /* this is newer */ if (head == NULL || has_exception(elem)) { struct carrot_t *rv; rv = carrot_alloc(); rv->elem = elem; rv->time = time; rv->next = head; return rv; } else { head->elem = elem; head->time = time; carrot_free(head->next); head->next = NULL; return head; } } else { if (has_exception(head->elem)) head->next = carrot_insert(head->next, elem, time, has_exception); return head; } } /* delete the carrot */ struct carrot_t *carrot_delete(struct carrot_t *head, struct vuht_entry_t *elem) { if (head == NULL) return NULL; else { if (head->elem == elem) { struct carrot_t *tail = head->next; head->next = NULL; carrot_free(head); return tail; } else { head->next = carrot_delete(head->next, elem); return head; } } } /* return the first element in carrot * which has no exceptions *OR* * whose exception function return false */ struct vuht_entry_t *carrot_check(struct carrot_t *head, int (*confirm)(struct vuht_entry_t *elem, void *opaque), void *opaque) { struct vuht_entry_t *ht = NULL; if (head != NULL) { struct carrot_t *curcar; for (curcar = head; curcar != NULL; curcar = curcar->next) { if (confirm(curcar->elem, opaque)) break; } if (curcar != NULL) ht = curcar->elem; carrot_free(head); } return ht; } vuos-0.9.2/umvu/src/epoch.c000066400000000000000000000046361476575172100155730ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include /* per thread time keeping */ __thread epoch_t virtual_epoch; /* epoch now is a (uint64_t) counter, it is used to timestamp all the state changes in the system * This global counter is incremented at each operation which modifies the "view". * Each value of the eposh is like a virtualization layer, this approach allows the support of * nested virtualization. See comments in epoch.h */ static epoch_t epoch_now = 2; /* one tick of the global timestamp clock epoch_now */ epoch_t update_epoch(void) { return __sync_fetch_and_add(&epoch_now, 1); } epoch_t set_vepoch(epoch_t e) { epoch_t tmp = virtual_epoch; virtual_epoch = e; return tmp; } epoch_t get_epoch(void) { return __sync_fetch_and_add(&epoch_now, 0); } void update_vepoch(void) { virtual_epoch = __sync_fetch_and_add(&epoch_now, 0); } epoch_t get_vepoch(void) { return virtual_epoch; } /* it is > 0 if the operation time is consistent with the service time. * in such a case it returns the epoch of the matching */ epoch_t matching_epoch(epoch_t service_epoch) { if (service_epoch < virtual_epoch) return service_epoch; else return 0; } static void *epoch_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_PTHREAD_CLONE: ret_value = &virtual_epoch; break; case INH_PTHREAD_START: virtual_epoch = *(epoch_t *) ioarg; break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_inheritance_upcall_register(epoch_upcall); } vuos-0.9.2/umvu/src/hashtable.c000066400000000000000000000432351476575172100164260ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*Hashtable object definition*/ /* vuht_entry_t: @obj: hash key @mtabline: mount tab line @type: type @trailingnumbers: boolean, match pathnames with trailing numbers @service: service associated to this item @service_hte: hte of the service associated to this item @private_data: opaque container for module data @objlen: len of the hash key @hashsum: hash sum for quick negative matching @count: usage count @confirmfun: confirmation function for exceptions @prev/next/pprevhash,nexthash: addresses for list linking */ struct vuht_entry_t { void *obj; char *mtabline; unsigned long mountflags; epoch_t timestamp; uint8_t type; int vuflags; struct vu_service_t *service; struct vuht_entry_t *service_hte; void *private_data; int objlen; long hashsum; _Atomic int count; /* confirmfun_t */ confirmfun_t confirmfun; struct vuht_entry_t *prev, *next, **pprevhash, *nexthash; }; #define VUHT_MTABLINE (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_SYNCHRONOUS | MS_REMOUNT | \ MS_MANDLOCK | MS_DIRSYNC | MS_NOATIME | MS_NODIRATIME | MS_POSIXACL | MS_RELATIME | MS_STRICTATIME | \ MS_LAZYTIME) #define VUHT_DELETED(ht) ((ht)->pprevhash == NULL) /* it must be a power of two (masks are used instead of modulo) */ #define VU_HASHTABLE_SIZE 512 #define VU_HASHTABLE_MASK (VU_HASHTABLE_SIZE-1) /* ReadWrite lock to access the Hashtable */ static pthread_rwlock_t vuht_rwlock = PTHREAD_RWLOCK_INITIALIZER; /* this is THE hash */ static struct vuht_entry_t *vuht_hash[VU_HASHTABLE_SIZE]; /* null tags have separate a separate list */ static struct vuht_entry_t *vuht_hash0[NCHECKS]; /* head of the list of hash entries */ static struct vuht_entry_t *vuht_head; /* lock for the list of hash entries */ static pthread_mutex_t vuht_head_lock = PTHREAD_MUTEX_INITIALIZER; static inline struct vuht_entry_t *vuht_alloc() { struct vuht_entry_t *rv = malloc(sizeof (struct vuht_entry_t)); fatal(rv); return rv; } /* free of vuht_entry_t */ static void vuht_free(struct vuht_entry_t *ht) { free(ht->obj); if (ht->mtabline) free(ht->mtabline); free(ht); } /* hash function */ /* hash sum and mod are separate functions: hash sums are used to quickly eliminate false positives, intermediate results can be completed during the scan */ static inline int hashmod(long hashsum) { return hashsum & VU_HASHTABLE_MASK; } /* djb2 hash function */ static inline long hashadd(long prevhash, char c) { return (((prevhash << 5) + prevhash) + c); } static inline long hashsum(uint8_t type, const char *c, int len) { long hash = type; int i; for (i = 0; i < len; i++, c++) hash = hashadd(hash, *c); return hash; } /* true if there are only trailing numbers (and there is at least one) */ /* View-OS permits "mount" of things like /dev/hda[0-9]* */ static inline int trailnum(char *s) { /* "at least one" the first element needs a special case. performance: >'9' is the most frequent case, <'0' are quite rare in pathnames, the end of string is more common */ int nonzero = 0; if (*s > '9' || *s == 0 || *s < '0') return 0; nonzero |= *s - '0'; for (s++; *s; s++) { if (*s > '9' || *s < '0') return 0; nonzero |= *s - '0'; } return nonzero; } /* vuht_internal_search computes the hash value adding one byte at a time. At each iteration it calls vuht_scan_stop to check if the current sequence is a valid node to be searched or not. This function returns 1 when the sequence is a logical subsequence for the type of object (a path prefix for pathnames, a heading subsequence if it is array of integers and so on */ static int vuht_scan_stop(uint8_t type, char *objc, int len, int exact) { switch (type) { case CHECKPATH: return ( *objc == 0 /* this is the end of a string */ || (!exact /* or when subtring match are allowed */ && (*objc == '/' /* test the match if the current char is '/' */ /* or if there are trailing numbers e.g. /dev/hda1, hda2 etc */ || trailnum(objc)))); case CHECKBINFMT: return (*objc == 0 /* this is the end of a string */ || (!exact /* or when subtring match are allowed */ && *objc == '/')); /* test the match if the current char is '/' */ case CHECKSOCKET: case CHECKCHRDEVICE: case CHECKBLKDEVICE: case CHECKSC: /* array of int, or null keys */ return ((len % sizeof(int)) == 0); case CHECKIOCTL: return ((len % sizeof(unsigned long)) == 0); case CHECKFSALIAS: /* end of string */ return (*objc == 0); case CHECKMODULE: if (exact) return (*objc == 0); else return 1; /* CHECKFSTYPE char by char */ default: return 0; } } /* terminate the scan */ static inline int vuht_scan_terminate(uint8_t type, char *objc, int len, int objlen) { switch (type) { case CHECKPATH: case CHECKBINFMT: case CHECKFSALIAS: case CHECKMODULE: return (*objc == 0); case CHECKSOCKET: case CHECKCHRDEVICE: case CHECKBLKDEVICE: case CHECKSC: case CHECKIOCTL: return (len == objlen); default: return 0; } } struct confirm_arg { uint8_t type; void *checkobj; int len; }; static int call_confirmfun(struct vuht_entry_t *ht, void *opaque) { confirmfun_t confirm = ht->confirmfun; if (confirm && ht->type != CHECKMODULE) { int rv; struct confirm_arg *args = opaque; epoch_t epoch = set_vepoch(ht->timestamp); if (ht->type == CHECKPATH) { char *path = args->checkobj; rv = confirm(args->type, path + ht->objlen, args->len, ht); } else rv = confirm(args->type, args->checkobj, args->len, ht); set_vepoch(epoch); return rv; } else return 1; } static int has_exception(struct vuht_entry_t *ht) { return ht->confirmfun != NULL && ht->type != CHECKMODULE; } static struct vuht_entry_t *vuht_internal_search(uint8_t type, void *obj, int objlen, void *checkobj, int exact) { struct vuht_entry_t *rv = NULL; char *objc = obj; long sum = type; long hash; struct carrot_t *carh = NULL; struct vuht_entry_t *ht; int len = 0; epoch_t e; pthread_rwlock_rdlock(&vuht_rwlock); /* scan the object one byte at a time, and check the hash table for each meaningful heading subsequence. The most specific (and more recent) matches are preferred. Hash elements may have exceptions: during this phase the scan generates a "carrot" of possible matches */ while (1) { if (vuht_scan_stop(type, objc, len, exact)) { hash = hashmod(sum); ht = (len) ? vuht_hash[hash] : vuht_hash0[type]; while (ht != NULL) { if (type == ht->type && sum == ht->hashsum && memcmp(obj, ht->obj, len) == 0 && ((ht->vuflags & VUFLAG_TRAILINGNUMBERS) || !trailnum(objc)) && (e = matching_epoch(ht->timestamp)) > 0) { /*carrot add*/ if (ht->confirmfun == NEGATIVE_MOUNT) carh = carrot_delete(carh, ht->private_data); else carh = carrot_insert(carh, ht, e, has_exception); } ht = ht->nexthash; } if (vuht_scan_terminate(type, objc, len, objlen)) break; } sum = hashadd(sum, *objc); objc++; len++; } /* if there is at least one (possbile) match, scan the "carrot" to confirm the match against exceptions */ if (carh != NULL) { struct confirm_arg args = { .type = type, .checkobj = checkobj, .len = len }; rv = carrot_check(carh, call_confirmfun, &args); } if (rv) rv->count++; pthread_rwlock_unlock(&vuht_rwlock); return rv; } /* search fuctions specific to eleemnt types */ static inline struct vuht_entry_t *vuht_pathsearch(uint8_t type, void *obj, int exact) { return vuht_internal_search(type, obj, 0, obj, exact); } static inline struct vuht_entry_t *vuht_binfmtsearch(uint8_t type, struct binfmt_req_t *req, int exact) { return vuht_internal_search(type, req->path, 0, req, exact); } static inline struct vuht_entry_t *vuht_search(uint8_t type, void *obj, int objlen, int exact) { return vuht_internal_search(type, obj, objlen, obj, exact); } static inline int stringobj(uint8_t type) { type &= ~PSEUDO_CHECK; return type == CHECKPATH || type == CHECKMODULE || type == CHECKFSALIAS; } static struct vuht_entry_t * internal_vuht_add(uint8_t type, const void *obj, int objlen, unsigned long mountflags, char *mtabline, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data) { struct vuht_entry_t **hashhead; struct vuht_entry_t *new = vuht_alloc(); /* create the entry and fill in the fields */ fatal(new); /* +1 if it is a string for the terminator */ new->obj = malloc(objlen + stringobj(type)); fatal(new->obj); memcpy(new->obj, obj, objlen); if (stringobj(type)) ((char *)new->obj)[objlen] = 0; new->objlen = objlen; new->type = type; new->mountflags = mountflags; new->mtabline = mtabline; new->vuflags = vuflags; new->private_data = private_data; new->service = service; new->service_hte = service->service_ht; new->confirmfun = confirmfun; new->count = !! (vuflags & VUFLAG_PERMANENT); if (service->service_ht) vuht_pick_again(service->service_ht); new->hashsum = hashsum(type, new->obj, new->objlen); pthread_rwlock_wrlock(&vuht_rwlock); /* timestamp must be updated in the critical section to avoid race conditions */ new->timestamp = update_epoch(); /* add it to the right hash collision list */ if (objlen==0) hashhead=&vuht_hash0[type]; else hashhead=&vuht_hash[hashmod(new->hashsum)]; if (*hashhead) (*hashhead)->pprevhash=&(new->nexthash); new->nexthash=*hashhead; new->pprevhash=hashhead; *hashhead=new; pthread_rwlock_unlock(&vuht_rwlock); /* add it to the list of hash entry of this type */ pthread_mutex_lock(&vuht_head_lock); if (vuht_head) { new->next=vuht_head->next; new->prev=vuht_head; new->next->prev=new; new->prev->next=new; vuht_head=new; } else vuht_head=new->next=new->prev=new; pthread_mutex_unlock(&vuht_head_lock); return new; } struct vuht_entry_t *vuht_add(uint8_t type, const void *obj, int objlen, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data) { return internal_vuht_add(type, obj, objlen, 0, NULL, service, vuflags, confirmfun, private_data); } static int permanent_mount(const char *opts) { char *match; if (opts == NULL) return 0; return (match = strstr(opts, "permanent")) != NULL && (match == opts || match[-1] == ',') && (match[9] == '\0' || match[9] == ','); } struct vuht_entry_t *vuht_pathadd(uint8_t type, const char *source, const char *path, const char *fstype, unsigned long mountflags, const char *mountopts, struct vu_service_t *service, int vuflags, confirmfun_t confirmfun, void *private_data) { char *mtabline; const char *addpath; struct vuht_entry_t *rv; if (source) { size_t optslen = mountflags2opts(mountflags & VUHT_MTABLINE, NULL, 0); char opts[optslen]; mountflags2opts(mountflags & VUHT_MTABLINE, opts, optslen); if (asprintf(&mtabline, "%s%s %s %s %s 0 %" PRIu64, (confirmfun == NEGATIVE_MOUNT) ? "-" : "", source, path, fstype, *opts == 0 ? "rw" : opts, get_epoch()) <= 0) { errno = ENOMEM; fatal(NULL); } } else mtabline = NULL; if (path[1] == '\0' && path[0] == '/') addpath = ""; else addpath = path; if (permanent_mount(mountopts)) vuflags |= VUFLAG_PERMANENT; rv = internal_vuht_add(type, addpath, strlen(addpath), mountflags, mtabline, service, vuflags, confirmfun, private_data); if (rv == NULL && mtabline != NULL) free(mtabline); return rv; } /* eliminate a deleted hash table element */ static int vuht_cleanup(struct vuht_entry_t *ht) { pthread_mutex_lock(&vuht_head_lock); if (ht == NULL) ht = vuht_head; if (ht == NULL) return -1; if (ht == vuht_head) { if (ht->next == ht) vuht_head = NULL; else vuht_head = ht->prev; } ht->prev->next = ht->next; ht->next->prev = ht->prev; ht->next = ht->prev = NULL; pthread_mutex_unlock(&vuht_head_lock); if (ht->service_hte && ht->service_hte != ht) { confirmfun_t service_cleanup = ht->service_hte->confirmfun; if (service_cleanup) { vu_mod_setht(ht); service_cleanup(ht->type, ht->obj, ht->objlen, ht); } vuht_drop(ht->service_hte); } if (ht->count == 0) vuht_free(ht); return 0; } /* unlink an element from the hash table */ static int vuht_del_locked(struct vuht_entry_t *ht, int umountflags) { int lazy = (umountflags & MNT_FORCE) || (umountflags & MNT_DETACH); if (!lazy && ht->count > 1) return -EBUSY; if (VUHT_DELETED(ht)) return -EINVAL; *(ht->pprevhash) = ht->nexthash; if (ht->nexthash) ht->nexthash->pprevhash = ht->pprevhash; ht->nexthash = NULL; ht->pprevhash = NULL; return 0; } int vuht_del(struct vuht_entry_t *ht, int umountflags) { if (ht) { int ret_value; pthread_rwlock_wrlock(&vuht_rwlock); ret_value = vuht_del_locked(ht, umountflags); pthread_rwlock_unlock(&vuht_rwlock); if (ret_value == 0 && ht->count == 0) vuht_cleanup(ht); return ret_value; } else return -ENOENT; } /* searching API */ struct vuht_entry_t *vuht_pick(uint8_t type, void *arg, struct vu_stat *st, int setepoch) { struct vuht_entry_t *hte; switch (type) { case CHECKPATH: hte = vuht_pathsearch(type, arg, 0); if (st != NULL) { if (__builtin_expect(S_ISCHR(st->st_mode), 0)) { struct vuht_entry_t *dhte = vuht_search(CHECKCHRDEVICE, &st->st_rdev, sizeof(dev_t), 0); if (dhte != NULL) { if (hte) hte->count--; hte = dhte; } } else if (__builtin_expect(S_ISBLK(st->st_mode), 0)) { struct vuht_entry_t *dhte = vuht_search(CHECKBLKDEVICE, &st->st_rdev, sizeof(dev_t), 0); if (dhte != NULL) { if (hte) hte->count--; hte = dhte; } } } break; case CHECKPATHEXACT: hte = vuht_pathsearch(CHECKPATH, arg, 1); break; case CHECKCHRDEVICE: case CHECKBLKDEVICE: hte = vuht_search(type, arg, sizeof(dev_t), 0); break; case CHECKSOCKET: case CHECKSC: hte = vuht_search(type, arg, sizeof(int), 0); break; case CHECKIOCTL: hte = vuht_search(type, arg, sizeof(unsigned long), 0); break; case CHECKFSALIAS: case CHECKMODULE: hte = vuht_search(type, arg, 0, 1); break; case CHECKFSTYPE: hte = vuht_search(CHECKMODULE, arg, 0, 0); break; case CHECKBINFMT: hte = vuht_binfmtsearch(type, arg, 0); break; default: hte = NULL; } if (hte && setepoch) set_vepoch(hte->timestamp); return hte; } void vuht_pick_again(struct vuht_entry_t *hte) { if (hte) hte->count++; } void vuht_drop(struct vuht_entry_t *hte) { if (hte) { if (--hte->count == 0 && VUHT_DELETED(hte)) vuht_cleanup(hte); } } /* reverse scan of hash table elements, useful to close all files */ static void forall_vuht_terminate(void) { while (vuht_cleanup(NULL) >= 0) ; } void forall_vuht_do(uint8_t type, void (*fun)(struct vuht_entry_t *ht, void *arg), void *arg) { pthread_rwlock_rdlock(&vuht_rwlock); if (vuht_head) { struct vuht_entry_t *scanht = vuht_head; do { scanht = scanht->next; if (scanht->type == type && !VUHT_DELETED(scanht) && (matching_epoch(scanht->timestamp)) > 0) fun(scanht, arg); } while (vuht_head != NULL && scanht != vuht_head); } pthread_rwlock_unlock(&vuht_rwlock); } /* mount table creation */ static void vuht_mtab_add(struct vuht_entry_t *ht, void *arg) { FILE *f = arg; if (ht->mtabline) fprintf(f, "%s\n", ht->mtabline); } void vuht_get_mtab(FILE *f) { if (f) forall_vuht_do(CHECKPATH, vuht_mtab_add, f); } const void *vuht_get_obj(struct vuht_entry_t *hte) { return hte->obj; } const char *vuht_path2mpath(struct vuht_entry_t *hte, const char *path) { if (__builtin_expect(hte != NULL && (hte->type & ~PSEUDO_CHECK) == CHECKPATH, 1)) { const char *retvalue = path + hte->objlen; return *retvalue == 0 ? "/" : retvalue; } else { printk(KERN_ERR, "path2mpath error"); return path; } } void *vuht_get_private_data(struct vuht_entry_t *hte) { if (hte) return hte->private_data; else return NULL; } void vuht_set_private_data(struct vuht_entry_t *hte, void *private_data) { if (hte) hte->private_data = private_data; } void vuht_set_service_cleanupfun(struct vuht_entry_t *hte, confirmfun_t cleanup_fun) { if (hte != NULL && hte->type == CHECKMODULE) { hte->confirmfun = cleanup_fun; } } struct vu_service_t *vuht_get_service(struct vuht_entry_t *hte) { if (hte) return hte->service; else return NULL; } unsigned long vuht_get_mountflags(struct vuht_entry_t *hte) { if (hte) return hte->mountflags; else return 0; } epoch_t vuht_get_vepoch(struct vuht_entry_t *hte) { return hte->timestamp; } int vuht_get_count(struct vuht_entry_t *hte) { return hte->count; } int vuht_get_objlen(struct vuht_entry_t *hte) { return hte->objlen; } void vuht_terminate(void) { forall_vuht_terminate(); } __attribute__((constructor)) static void init (void) { vu_destructor_register(vuht_terminate); } vuos-0.9.2/umvu/src/linux_32_64.c000066400000000000000000000035611476575172100164450ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include /* convert dirent64 to dirent: VUOS modules handle 64bit dirents, this conversion is needed to support 32bit dirent (on 32bit architectures) */ void dirent64_to_dirent(void* buf, int count){ struct linux_dirent *dirp=buf; struct dirent64 *dirp64=buf; int counter=0; unsigned short int buf_len; /* Actually the conversion is a bit tricky. * dirent is always shorter then dirent64. * copy the corresponding fields * keep the record length (there will be some unused bytes) */ for( counter=0; counterd_ino = (unsigned long) dirp64->d_ino; dirp->d_off = (unsigned long) dirp64->d_off; buf_len = dirp->d_reclen = dirp64->d_reclen; tmptype = dirp64->d_type; memmove(dirp->d_name,dirp64->d_name,strlen(dirp64->d_name)+1); *((char *) dirp + buf_len - 1)=tmptype; counter= counter + dirp->d_reclen; //bad... dirp = (struct linux_dirent *) ((char*)dirp + buf_len); dirp64 = (struct dirent64 *) ((char*)dirp64 + buf_len); } } vuos-0.9.2/umvu/src/mountflags.c000066400000000000000000000054351476575172100166520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include /* translate all mount flags into options so that modules can parse options only */ char *mountflag_strings[32] = { "ro", // 1 "nosuid", // 2 "nodev", // 4 "noexec", // 8 "sync", // 16 "remount", // 32 "mand", // 64 "dirsync", // 128 NULL, // 256 NULL, // 512 "noatime", // 1024 "nodiratime", // 2048 "bind", // 4096 "move", // 8192 "rec", // 16384 "silent", // 32768 "acl", // 1 << 16 "unbindable", // 1 << 17 NULL, // 1 << 18 (MS_PRIVATE) NULL, // 1 << 19 (MS_SLAVE) NULL, // 1 << 20 (MS_SHARED) "relatime", // 1 << 21 NULL, // 1 << 22 (MS_KERNMOUNT) "iversion", // 1 << 23 (MS_IVERSION) "strictatime", // 1 << 24 "lazytime", // 1 << 25 NULL, // 1 << 26 NULL, // 1 << 27 NULL, // 1 << 28 NULL, // 1 << 29 NULL, // 1 << 30 (MS_ACTIVE) "nouser", // 1 << 31 (MS_NOUSER) }; static char *strlpcpy(char *dst, char *src, char *limit) { while (*src && dst < limit - 1) *dst++ = *src++; *dst = 0; return dst; } size_t mountflags2opts(unsigned long mountflags, char *opts, size_t optslen) { int i; if (opts) { char *limit = opts + optslen; char *nextopt = opts; *nextopt = 0; if (mountflags & 1) nextopt = strlpcpy(nextopt, mountflag_strings[0], limit); else nextopt = strlpcpy(nextopt, "rw", limit); for (i = 1; i < 32; i++) { if ((mountflags & (1UL << i)) && (mountflag_strings[i])) { nextopt = strlpcpy(nextopt, ",", limit); nextopt = strlpcpy(nextopt, mountflag_strings[i], limit); } } return (nextopt - opts) + 1; } else { size_t retval = 3; for (i = 1; i < 32; i++) { if ((mountflags & (1UL << i)) && (mountflag_strings[i])) retval += strlen(mountflag_strings[i]) + 1; } return retval; } } #if 0 int main() { unsigned long flags = 0; //0xffffffff; size_t len = mountflags2opts(flags, NULL, 0); char str[len]; printf("%d %d\n", len, mountflags2opts(flags, str, len)); printf("%s %d\n", str, strlen(str)); } #endif vuos-0.9.2/umvu/src/path_utils.c000066400000000000000000000220221476575172100166360ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_REALPATH_FLAGS (PERMIT_NONEXISTENT_LEAF | CHECK_S_IXALL_ON_DIRS) struct realpath_arg_t { int dirfd; uint8_t nested; uint8_t need_rewrite; struct vu_stat *statbuf; }; static inline int realpath_flags(int flags) { return DEFAULT_REALPATH_FLAGS | (flags & (FOLLOWLINK | PERMIT_EMPTY_PATH)); } /* get a pathname from a user space process and canonicalize it */ char *get_path(int dirfd, syscall_arg_t addr, struct vu_stat *buf, int flags, uint8_t *need_rewrite, int nested) { char pathbuf[nested ? 0 : PATH_MAX]; char *path; struct vu_stat nullbuf[buf == NULL]; struct realpath_arg_t realpath_arg = { .dirfd = dirfd, .nested = nested, .need_rewrite = 0, .statbuf = (buf == NULL) ? nullbuf : buf}; char *ret_value; if (nested) path = (char *) addr; else { path = pathbuf; umvu_peek_str(addr, path, PATH_MAX); } ret_value = canon_realpath_dup(path, realpath_flags(flags), &realpath_arg); printkdebug(p,"get_%spath %d %s->%s errno:%d epoch:0x%lx rewr:%d", nested ? "nested_" : "", dirfd, path, ret_value, errno, get_vepoch(), realpath_arg.need_rewrite); if (need_rewrite) *need_rewrite = realpath_arg.need_rewrite; return ret_value; } /* special case (type == 3) in vu_arch_table. * ARCH_TYPE_SYMLINK_NOFOLLOW can be set on or off depending on a specific flag */ int path_check_exceptions(int syscall_number, syscall_arg_t *args) { int nargs = vu_arch_table_nargs(syscall_number); switch (syscall_number) { case __NR_openat: return (args[2] & O_NOFOLLOW) ? 3 : 2; case __NR_open: return (args[1] & O_NOFOLLOW) ? 1 : 0; case __NR_umount2: return (args[1] & UMOUNT_NOFOLLOW) ? 1 : 0; case __NR_unlinkat: case __NR_readlinkat: return 3; case __NR_statx: return (args[2] & AT_SYMLINK_NOFOLLOW) ? 3 : 2; default: return (args[nargs-1] & AT_SYMLINK_NOFOLLOW) ? 3 : 2; } } /* Some at- syscalls support AT_EMPTY_PATH flags. If the path is empty these system calls operate on the dirfd file */ static inline int is_at_empty_path(int syscall_number, syscall_arg_t *args) { int nargs = vu_arch_table_nargs(syscall_number); switch (syscall_number) { case __NR_openat: case __NR_open: case __NR_umount2: case __NR_unlinkat: case __NR_readlinkat: return 0; case __NR_statx: return (args[2] & AT_EMPTY_PATH); default: return (args[nargs-1] & AT_EMPTY_PATH); } } /* get the pathname argument of a system call, canonicalize it. The return value is a dynamically allocated string */ char *get_syspath(struct syscall_descriptor_t *sd, struct vu_stat *buf, uint8_t *need_rewrite) { int syscall_number = sd->syscall_number; int patharg = vu_arch_table_patharg(syscall_number); int dirfd = AT_FDCWD; if (patharg < 0) { errno = 0; return NULL; } else { int flags = FOLLOWLINK; int type = vu_arch_table_type(syscall_number); if (type == 3) type = path_check_exceptions(syscall_number, sd->syscall_args); if (type & ARCH_TYPE_SYMLINK_NOFOLLOW) flags &= ~FOLLOWLINK; if (type & ARCH_TYPE_IS_AT) { dirfd = sd->syscall_args[patharg]; patharg++; if (is_at_empty_path(syscall_number, sd->syscall_args)) flags |= PERMIT_EMPTY_PATH; } return get_path(dirfd, sd->syscall_args[patharg], buf, flags, need_rewrite, VU_NOT_NESTED); } } /* get the pathname argument of a *nested* system call, canonicalize it. The return value is a dynamically allocated string */ char *get_nested_syspath(int syscall_number, syscall_arg_t *args, struct vu_stat *buf, uint8_t *need_rewrite) { int patharg = vu_arch_table_patharg(syscall_number); int dirfd = AT_FDCWD; if (patharg < 0) { errno = 0; return NULL; } else { int flags = FOLLOWLINK; int type = vu_arch_table_type(syscall_number); if (type == 3) type = path_check_exceptions(syscall_number, args); if (type & ARCH_TYPE_SYMLINK_NOFOLLOW) flags &= ~FOLLOWLINK; if (type & ARCH_TYPE_IS_AT) { dirfd = args[patharg]; patharg++; if (is_at_empty_path(syscall_number, args)) flags |= PERMIT_EMPTY_PATH; } return get_path(dirfd, args[patharg], buf, flags, need_rewrite, VU_NESTED); } } /* get the pathname argument of a *virtual* system call, canonicalize it. The return value is a dynamically allocated string */ char *get_vsyspath(struct syscall_descriptor_t *sd, struct vu_stat *buf, uint8_t *need_rewrite) { int syscall_number = -sd->syscall_number; int patharg = vvu_arch_table_patharg(syscall_number); int dirfd = AT_FDCWD; if (patharg < 0) { errno = 0; return NULL; } else { int flags = FOLLOWLINK; int type = vvu_arch_table_type(syscall_number); if (type & ARCH_TYPE_SYMLINK_NOFOLLOW) flags &= ~FOLLOWLINK; return get_path(dirfd, sd->syscall_args[patharg], buf, flags, need_rewrite, VU_NOT_NESTED); } } /* rewrite the pathname argument of a system call. newpath must be an absolute pathname */ void rewrite_syspath(struct syscall_descriptor_t *sd, char *newpath) { int syscall_number = sd->syscall_number; int patharg = vu_arch_table_patharg(syscall_number); if (patharg >= 0) { int type = vu_arch_table_type(syscall_number); if (type == 3) type = path_check_exceptions(syscall_number, sd->syscall_args); if (type & ARCH_TYPE_IS_AT) patharg++; sd->syscall_args[patharg] = vu_push(sd, newpath, strlen(newpath) + 1); } } /* canonicalize's helper functions */ static inline mode_t get_lmode(struct vuht_entry_t *ht, const char *pathname, struct vu_stat *buf) { int stat_retval; if (ht) stat_retval = service_syscall(ht,__VU_lstat)(vuht_path2mpath(ht, pathname), buf, AT_SYMLINK_NOFOLLOW, -1, NULL); else stat_retval = r_vu_lstat(pathname, buf); if (stat_retval < 0) buf->st_mode = 0; return buf->st_mode; } static mode_t vu_lmode(const char *pathname, void *private) { struct vuht_entry_t *ht; struct realpath_arg_t *arg = private; epoch_t e = get_vepoch(); struct vuht_entry_t *sht = vu_mod_getht(); mode_t retval; ht = vuht_pick(CHECKPATH, (char *)pathname, NULL, SET_EPOCH); vu_mod_setht(ht); retval = get_lmode(ht, pathname, arg->statbuf); /* ALTERNATIVE #1: use vu_access_emu to decide if the dir is XOK */ #if 0 if (S_ISDIR(retval) && (retval & S_IXALL) != S_IXALL && vu_access_emu(buf, X_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW) == 0) retval |= S_IXALL; #endif /* ALTERNATIVE #2: mimic root ok to cd is it is OKX for somebody */ if (S_ISDIR(retval) && (retval & S_IXALL) != 0) retval |= S_IXALL; if (ht) { arg->need_rewrite = 1; vuht_drop(ht); } set_vepoch(e); vu_mod_setht(sht); return retval; } static ssize_t vu_readlink(const char *pathname, char *buf, size_t bufsiz, void *private) { struct vuht_entry_t *ht; epoch_t e = get_vepoch(); struct vuht_entry_t *sht = vu_mod_getht(); ssize_t retval; ht = vuht_pick(CHECKPATH, (char *)pathname, NULL, SET_EPOCH); vu_mod_setht(ht); if (ht) { retval = service_syscall(ht, __VU_readlink)(vuht_path2mpath(ht, pathname), buf, bufsiz); vuht_drop(ht); } else retval = r_readlink(pathname, buf, bufsiz); set_vepoch(e); vu_mod_setht(sht); return retval; } static int vu_getcwd(char *pathname, size_t size, void *private) { struct realpath_arg_t *arg = private; if (arg->dirfd == AT_FDCWD) { if (arg->nested == 0) { vu_fs_get_cwd(pathname, size); return 0; } else return getcwd(pathname, size) == 0 ? -1 : 0; } else { vu_fd_get_path(arg->dirfd, arg->nested, pathname, size); if (*pathname == 0) strcpy(pathname, "/"); return 0; } } static int vu_getroot(char *pathname, size_t size, void *private) { struct realpath_arg_t *arg = private; if (arg->nested) { pathname[0] = '/'; pathname[1] = '\0'; } else vu_fs_get_rootdir(pathname, size); return 0; } __attribute__((constructor)) static void init(void) { struct canon_ops ops = { .lmode = vu_lmode, .readlink = vu_readlink, .getcwd = vu_getcwd, .getroot = vu_getroot, }; canon_setops(&ops); debug_set_name(p, "PATH"); } vuos-0.9.2/umvu/src/r_table.c000066400000000000000000000025741476575172100161040ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include /* r_table.h is automatically generated during the build process. * r_ stands for real system calls. * All the system call requestsgenerated by the hypervisor can be further * virtualized ***unless** they are real r_ system calls. * e.g. open(path ... ), may be processed by a module * r_open(path ...) always refers to the file "path" in the hosting operating system */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" long (*native_syscall)() = syscall; #pragma GCC diagnostic pop vuos-0.9.2/umvu/src/service.c000066400000000000000000000070051476575172100161260ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include /* helper functions for modules */ pid_t vu_mod_gettid() { return umvu_gettid(); } mode_t vu_mod_getumask(void) { return vu_fs_get_umask(); } mode_t vu_mod_getmode() { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); return sd->extra->statbuf.st_mode; } struct vuht_entry_t *vu_mod_getht(void) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); return sd->extra->ht; } void vu_mod_setht(struct vuht_entry_t *ht) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); sd->extra->ht = ht; } /* modules are not aware of direct or nested calls */ int vu_mod_getsyscall_number(void) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); return sd->syscall_number; } syscall_arg_t vu_mod_getsyscall_arg(unsigned int narg) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); if (narg < 6) return sd->syscall_args[narg]; else return 0; } void vu_mod_peek_str(void *addr, void *buf, size_t datalen) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; if (nested) strncpy(buf, addr, datalen); else umvu_peek_str((uintptr_t) addr, buf, datalen); } char *vu_mod_peekdup_path(void *addr) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; if (nested) return strdup(addr); else { char path[PATH_MAX]; if (umvu_peek_str((uintptr_t) addr, path, PATH_MAX) == 0) return strdup(path); else return NULL; } } void vu_mod_peek_data(void *addr, void *buf, size_t datalen) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; if (nested) memcpy(buf, addr, datalen); else umvu_peek_data((uintptr_t) addr, buf, datalen); } void vu_mod_poke_data(void *addr, void *buf, size_t datalen) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; if (nested) memcpy(addr, buf, datalen); else umvu_poke_data((uintptr_t) addr, buf, datalen); } int vu_mod_fd_get_sfd(int fd, void **fdprivate) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; return vu_fd_get_sfd(fd, fdprivate, nested); } struct vuht_entry_t *vu_mod_fd_get_ht(int fd) { struct syscall_descriptor_t *sd = get_thread_sd(); fatal(sd); fatal(sd->extra); int nested = sd->extra->nested; struct vuht_entry_t *rv = vu_fd_get_ht(fd, nested); return rv; } vuos-0.9.2/umvu/src/umvu_main.c000066400000000000000000000140171476575172100164670ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* RISCV seccomp tracing porting is incomplete */ # define SECCOMP_ARG 128 #if defined(__riscv_xlen) && __riscv_xlen == 64 # define SECCOMP_DEFAULT 0 #else # define SECCOMP_DEFAULT 1 #endif static char *progname; static char *short_options = "+hxNSl:s:f:V:o:d:D:"; static struct option long_options[] = { {"help",no_argument, 0, 'h'}, {"nonesting",no_argument, 0, 'x'}, {"rc",required_argument, 0, 'f'}, {"norc",no_argument, 0, 'N'}, {"output", required_argument, 0, 'o'}, {"loglevel",required_argument, 0, 'l'}, {"syslog",required_argument, 0, 's'}, {"vu_name",required_argument,0,'V'}, {"debugtags",required_argument,0,'d'}, {"debugcols",required_argument,0,'D'}, {"noseccomp",no_argument,0,'S'}, {"seccomp",no_argument,0,SECCOMP_ARG}, {0,0,0,0}}; static void usage_n_exit(void) { fprintf(stderr, "UMVU: user mode implementation of VU-OS\n" "Version %s - Copyright 2017-2025 VirtualSquare Team\n\n" "Usage:\n" " %s OPTIONS cmd args\n\n" " -h --help print this short usage message\n" " -x --nonesting disable nested virtualization support\n" " -f file\n" " --rc file set initialization file\n" " -n --norc do not load standard inizialization files\n" " -l --loglevel set log level (*)\n" " -s --syslog set syslog level (*)\n" " -V name\n" " --vu_name name define the view name (see vuname)\n" " -o outfile\n" " --output outfile redirect console output to outfile\n" " -d tags\n" " --debugtags tags define the active debug tags (see vudebug)\n" " -D cols\n" " --debugcols cols define the debug color string (see vudebug)\n" " -S --noseccomp disable_seccomp_optimization\n" "\n" "(*) 0:emerg 1:alert 2:crit 3:err 4-warning 5:notice 6:info 7:debug\n" "\n" , VUOS_VERSION, progname); r_exit(1); } /* First scan of the arg list to manage some option before the reloading of umvu (to enable purelibc and thus the nested virtualization) */ static void early_args(int argc, char *argv[]) { int c; int nesting __attribute__((unused)) = 1; while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case '?': case 'h': usage_n_exit(); break; case 'x': nesting = 0; break; } } /* enable nested virtualization: reload umvu */ if (nesting) vu_nesting_init(argc, argv); } static void runrc(const char *path) { if (faccessat(AT_FDCWD, path, X_OK, AT_EACCESS)==0) { int pid; int status; switch (pid=fork()) { case -1: exit(2); case 0: execl(path,path,(char *)0); exit(2); default: r_wait4(pid, &status, 0, NULL); if (!WIFEXITED(status)) exit(2); } } } int main(int argc, char *argv[]) { int c; int norc = 0; int seccomp = SECCOMP_DEFAULT; char *output_file = NULL; char *rcfile = NULL; char *vu_name = NULL; int childpid; progname = basename(argv[0]); early_args(argc, argv); /* rewind args */ optind = 1; while (1) { int option_index = 0; c = getopt_long(argc, argv, short_options, long_options, &option_index); if (c == -1) break; switch (c) { case '?': usage_n_exit(); break; case 'd': debug_add_tags(optarg, 0); break; case 'D': debug_set_color_string(optarg); break; case 'l': set_console_log_level(atoi(optarg)); break; case 's': set_syslog_log_level(atoi(optarg)); break; case 'o': output_file = optarg; break; case 'f': rcfile = optarg; break; case 'V': vu_name = optarg; break; case 'N': norc = 1; break; case 'S': seccomp = 0; break; case SECCOMP_ARG: seccomp = 1; break; } } argc -= optind; argv += optind; if (vu_name) set_vu_name(vu_name); if (output_file) { set_log_file(output_file); } if (seccomp && umvu_tracer_test_seccomp() < 0) { printk(KERN_WARNING "seccomp_filter unavailable, use legacy ptrace instead\n"); seccomp = 0; } childpid = umvu_tracer_fork(seccomp); if (childpid < 0) exit(1); else if (childpid > 0) { /* parent = tracer */ int wstatus; vu_nesting_enable(); vu_init(); wstatus = umvu_tracepid(childpid, vu_syscall_execute, 1); vu_fini(); r_exit(WEXITSTATUS(wstatus)); } else { /* child: this is the root of all the traced processes */ /* disable purelibc */ vu_nesting_disable(); /* run rcfile or default rc files: .vurc in home dir and /etc/vurc */ if (rcfile) runrc(rcfile); else if (norc == 0) { char *home = getenv("HOME"); runrc(ETC_VURC); if (home != NULL) { int homerc_len = strlen(home) + strlen(VURC) + 3; char homerc[homerc_len]; snprintf(homerc, homerc_len, "%s/.%s", getenv("HOME"), VURC); runrc(homerc); } } execvp(argv[0], argv); printk(KERN_ERR "%s: %s\n", argv[0], strerror(errno)); return 1; } } vuos-0.9.2/umvu/src/umvu_peekpoke.c000066400000000000000000000202561476575172100173500ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include /* pagesize/pagemask are useful to compute the page boundaries. data tranfer operations are splitted in chunks, all the addresses of a chunk belong to the same page */ /* pagesize/pagemask values are cached here at startup for performance (these values cannnot change during the life of the hypervisor */ static unsigned long page_size; static unsigned long page_mask; /* tid of the "protected" process (each guardian angel controls the virtualization of a user process/thread) */ static __thread unsigned int tracee_tid; #if defined(__x86_64__) void umvu_peek_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op) { if (regs && syscall_desc) { if (op == PEEK_ARGS) { syscall_desc->orig_syscall_number = syscall_desc->syscall_number = regs->orig_rax; syscall_desc->syscall_args[0] = regs->rdi; syscall_desc->syscall_args[1] = regs->rsi; syscall_desc->syscall_args[2] = regs->rdx; syscall_desc->syscall_args[3] = regs->r10; syscall_desc->syscall_args[4] = regs->r8; syscall_desc->syscall_args[5] = regs->r9; syscall_desc->prog_counter = regs->rip; syscall_desc->stack_pointer = regs->rsp; } else syscall_desc->orig_ret_value = regs->rax; } } int umvu_poke_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op) { if (regs && syscall_desc) { switch (op) { case POKE_ARGS: /* regs->rsp is missing as stack pointer should not be modified */ if (regs->orig_rax == (unsigned) syscall_desc->syscall_number && regs->rdi == syscall_desc->syscall_args[0] && regs->rsi == syscall_desc->syscall_args[1] && regs->rdx == syscall_desc->syscall_args[2] && regs->r10 == syscall_desc->syscall_args[3] && regs->r8 == syscall_desc->syscall_args[4] && regs->r9 == syscall_desc->syscall_args[5] && regs->rip == syscall_desc->prog_counter) return 0; regs->orig_rax = regs->rax = syscall_desc->syscall_number; regs->rdi = syscall_desc->syscall_args[0]; regs->rsi = syscall_desc->syscall_args[1]; regs->rdx = syscall_desc->syscall_args[2]; regs->r10 = syscall_desc->syscall_args[3]; regs->r8 = syscall_desc->syscall_args[4]; regs->r9 = syscall_desc->syscall_args[5]; regs->rip = syscall_desc->prog_counter; break; case POKE_RETVALUE: if (regs->rax == syscall_desc->ret_value) return 0; regs->rax = syscall_desc->ret_value; break; case SKIP_SETRETVALUE: regs->orig_rax = -1; regs->rax = syscall_desc->ret_value; break; } return 1; } else return 0; } #elif defined(__riscv_xlen) && __riscv_xlen == 64 void umvu_peek_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op) { if (regs && syscall_desc) { if (op == PEEK_ARGS) { syscall_desc->orig_syscall_number = syscall_desc->syscall_number = regs->a7; syscall_desc->syscall_args[0] = regs->a0; syscall_desc->syscall_args[1] = regs->a1; syscall_desc->syscall_args[2] = regs->a2; syscall_desc->syscall_args[3] = regs->a3; syscall_desc->syscall_args[4] = regs->a4; syscall_desc->syscall_args[5] = regs->a5; syscall_desc->prog_counter = regs->pc; syscall_desc->stack_pointer = regs->sp; } else syscall_desc->orig_ret_value = regs->a0; } } int umvu_poke_syscall(arch_regs_struct *regs, struct syscall_descriptor_t *syscall_desc, peekpokeop_t op) { if (regs && syscall_desc) { switch (op) { case POKE_ARGS: /* regs->sp is missing as stack pointer should not be modified */ if (regs->a7 == (unsigned) syscall_desc->syscall_number && regs->a0 == syscall_desc->syscall_args[0] && regs->a1 == syscall_desc->syscall_args[1] && regs->a2 == syscall_desc->syscall_args[2] && regs->a3 == syscall_desc->syscall_args[3] && regs->a4 == syscall_desc->syscall_args[4] && regs->a5 == syscall_desc->syscall_args[5] && regs->pc == syscall_desc->prog_counter) return 0; regs->a7 = syscall_desc->syscall_number; regs->a0 = syscall_desc->syscall_args[0]; regs->a1 = syscall_desc->syscall_args[1]; regs->a2 = syscall_desc->syscall_args[2]; regs->a3 = syscall_desc->syscall_args[3]; regs->a4 = syscall_desc->syscall_args[4]; regs->a5 = syscall_desc->syscall_args[5]; regs->pc = syscall_desc->prog_counter; break; case POKE_RETVALUE: if (regs->a0 == syscall_desc->ret_value) return 0; regs->a0 = syscall_desc->ret_value; break; case SKIP_SETRETVALUE: regs->a7 = -1; regs->a0 = syscall_desc->ret_value; break; } return 1; } else return 0; } #else #error Unsupported architecture #endif void umvu_settid(pid_t tid) { tracee_tid = tid; } pid_t umvu_gettid() { return tracee_tid; } void umvu_unblock(void) { P_INTERRUPT(tracee_tid, 0L); } void umvu_block(struct syscall_descriptor_t *sd) { sd->syscall_number = __NR_ppoll; sd->syscall_args[0] = 0; sd->syscall_args[1] = 0; sd->syscall_args[2] = 0; //NULL sd->syscall_args[3] = 0; //NULL } /* return len or the offset of the next page boundary, whatever is nearer to addr */ static inline long compute_chunk_len(uintptr_t addr, size_t len) { unsigned long chunk_len = len > page_size ? page_size : len; unsigned long end_in_page = ((uintptr_t)(addr + chunk_len) & page_mask); if (chunk_len > end_in_page) chunk_len -= end_in_page; return chunk_len; } int umvu_peek_str(uintptr_t addr, void *buf, size_t datalen) { char *cbuf = buf; if (addr && cbuf) { /* transfer data, chunk by chunk */ while (datalen > 0) { unsigned long chunk_len = compute_chunk_len(addr, datalen); struct iovec local_iov = {cbuf, chunk_len}; struct iovec remote_iov = {(void *) addr, chunk_len}; int rv=r_process_vm_readv(tracee_tid, &local_iov, 1, &remote_iov, 1, 0); if (rv != (int) chunk_len) return -1; else { unsigned int r; /* it is a string, when there is a NULL byte, the transfer is completed */ for (r = 0; r < chunk_len; r++) if (cbuf[r] == 0) return 0; datalen -= chunk_len; addr += chunk_len; cbuf += chunk_len; } } cbuf[-1] = 0; } return 0; } char *umvu_peekdup_path(uintptr_t addr) { char path[PATH_MAX]; if (umvu_peek_str(addr, path, PATH_MAX) == 0) return strdup(path); else return NULL; } int umvu_peek_data(uintptr_t addr, void *buf, size_t datalen) { char *cbuf = buf; if (addr && cbuf) { /* transfer data, chunk by chunk */ while (datalen > 0) { unsigned long chunk_len = compute_chunk_len(addr, datalen); struct iovec local_iov = {cbuf, chunk_len}; struct iovec remote_iov = {(void *) addr, chunk_len}; int rv=r_process_vm_readv(tracee_tid, &local_iov, 1, &remote_iov, 1, 0); if (rv != (int) chunk_len) return -1; else { datalen -= chunk_len; addr += chunk_len; cbuf += chunk_len; } } } return 0; } int umvu_poke_data(uintptr_t addr, void *buf, size_t datalen) { if (addr && buf) { struct iovec local_iov = {buf, datalen}; struct iovec remote_iov = {(void *) addr, datalen}; return r_process_vm_writev(tracee_tid, &local_iov, 1, &remote_iov, 1, 0) >= 0; } else return 0; } unsigned long umvu_get_pagesize(void) { return page_size; } __attribute__((constructor)) static void __init__(void) { page_size = sysconf(_SC_PAGESIZE); page_mask = page_size - 1; } vuos-0.9.2/umvu/src/umvu_tracer.c000066400000000000000000000517201476575172100170250ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017,2018,2019 Renzo Davoli , * Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DISABLE_VDSO static int umvu_trace_legacy(pid_t tracee_tid); static int umvu_trace_seccomp(pid_t tracee_tid); static int (*umvu_trace)(pid_t) = umvu_trace_legacy; static enum __ptrace_request ptrace_next_syscall = PTRACE_SYSCALL; /* pthread_create is processed by vu_nesting to support threads of the hypervisor started by modules. libc_pthread_create is the real pthread_create provided by the libc. The tracer threads created by the tracer for each user-level thread/process should not be processed by vu_nesting */ static int (*libc_pthread_create)(); static int nproc; static pthread_mutex_t nproc_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t nproc_termination_cond = PTHREAD_COND_INITIALIZER; static void default_syscall_handler(syscall_state_t state, struct syscall_descriptor_t *sd); static syscall_handler_t syscall_handler = default_syscall_handler; /* This BPF filter: returns SECCOMP_RET_ALLOW if the syscall is restart_syscall or poll(1, _, _). it returns SECCOMP_RET_TRACE otherwise. */ /* All syscalls are forwarded via ptrace to the hypervisor (but restart_syscall or the special case of poll to manage the hand-off between guardian angels) */ /* The hypervisor: * changes the syscall number to -1 to skip the syscall * returns PTRACE_CONT if the syscall is real (the kernel must process it) * returns PTRACE_SYSCALL if the kernel must process it but post processing is needed */ static struct sock_filter seccomp_filter[] = { BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_restart_syscall, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ppoll, 0, 3), BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, args[0])), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 1, 0, 1), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE), }; static struct sock_fprog seccomp_prog = { .filter = seccomp_filter, .len = (unsigned short) (sizeof(seccomp_filter)/sizeof(seccomp_filter[0])), }; #ifdef DISABLE_VDSO /* vdso is a small memory chunk of memory shared with the kernel. * syscalls like gettimeofday use vdso when available but in this way * that syscalls cannnot be ptraced and thus virtualized. * this code disables vdso support. It resets the value of the aux * tag AT_SYSINFO_EHDR to 0 (it is the address of the vdso page). */ #include static void disable_vdso(pid_t tid) { arch_regs_struct regs; struct syscall_descriptor_t sys_orig; uintptr_t addr; uintptr_t string; struct auxel { uintptr_t key; uintptr_t value; } auxel; P_GETREGS_NODIE(tid, ®s); umvu_peek_syscall(®s, &sys_orig, PEEK_ARGS); //printk("disable_vdso %d %llx\n", tid, sys_orig.stack_pointer); string = -1; /* argv */ for (addr = sys_orig.stack_pointer; string != 0; addr += sizeof(string)) { umvu_peek_data(addr, &string, sizeof(string)); //printk("env %llx\n", string); } string = -1; /* environ */ for (; string != 0; addr += sizeof(string)) { umvu_peek_data(addr, &string, sizeof(string)); //printk("env %llx\n", key); } auxel.key = -1; for (; auxel.key != 0; addr += sizeof(auxel)) { umvu_peek_data(addr, &auxel, sizeof(auxel)); //printk("env %llx %llx\n", auxel.key, auxel.value); if (auxel.key == AT_SYSINFO_EHDR) { auxel.value = 0; umvu_poke_data(addr, &auxel, sizeof(auxel)); break; } } } #else #define disable_vdso(x) #endif static void default_syscall_handler(syscall_state_t state, struct syscall_descriptor_t *sd) { #if 0 printf("trace %d - SCNO %d\n", state, sd->syscall_number); sd->action = DOIT_CB_AFTER; if (state == OUT_SYSCALL) sd->ret_value = sd->orig_ret_value; #endif } static void nproc_update(int i) { pthread_mutex_lock(&nproc_mutex); nproc += i; if (nproc == 0) pthread_cond_broadcast(&nproc_termination_cond); pthread_mutex_unlock(&nproc_mutex); } /* The hypervisor waits for the termination of oll the threads to exit */ static void wait4termination(void) { pthread_mutex_lock(&nproc_mutex); while (nproc > 0) pthread_cond_wait(&nproc_termination_cond, &nproc_mutex); pthread_mutex_unlock(&nproc_mutex); } /* This structure provides a new tracer thread with all the data needed */ typedef struct tracer_args { pid_t tracee_tid; arch_regs_struct regs; void *inherited_args[]; } tracer_args; /* block/unblock_tracee are used: * * during the handoff between tracer threads * * to manage blocking system calls requests (like poll/select/accept or * read from devices or sockets.) * management of blocking syscall requests (issued by user processes): * TRACEE: the syscall is changed into ppoll(1, 0, NULL, NULL) * TRACER: a waiting thread is created (its pid is in waiting_pid), * this thread waits the expected event using epoll, and then exits. * This wait state can end in two different ways: * TRACER: the expected (virtual) event arrives, the waiting thread terminates. then * the tracer gets the child termination signal, the tracee is awaken * by a P_INTERRUPT * TRACEE: The ppoll syscall terminates for an external event (e.g. SIGINT). * (or for the virtualization of poll/select etc, when an awaited event * of a rea file descriptor arrives). * the tracers gets the completion of the ppoll and kills the waiting_pid. */ static void unblock_tracee(pid_t tid, arch_regs_struct *regs) { P_INTERRUPT(tid, 0L); r_wait4(tid, NULL, __WALL, NULL); P_SETREGS(tid, regs); // P_SYSCALL if legacy, P_CONT if SECCOMP PTRACE(ptrace_next_syscall, tid, 0L, 0L); } static void *spawn_tracer(void *arg) { tracer_args *t_arg = (tracer_args *)arg; pid_t tracee_tid = t_arg->tracee_tid; umvu_settid(tracee_tid); nproc_update(1); vu_inheritance_call(INH_START, t_arg->inherited_args, &tracee_tid); P_SEIZE_NODIE(tracee_tid, PTRACE_STD_OPTS); unblock_tracee(tracee_tid, &(t_arg->regs)); free(t_arg); umvu_trace(tracee_tid); return NULL; } static void block_tracee(pid_t tid, arch_regs_struct *regs) { struct syscall_descriptor_t sys_orig, sys_modified; P_GETREGS_NODIE(tid, regs); umvu_peek_syscall(regs, &sys_orig, PEEK_ARGS); sys_modified = sys_orig; /* change syscall to poll(NULL, 0, -1); * actually it uses ppoll((struct pollfd *)1, 0, NULL, NULL): * the first arg is ignored as the second is zero. * the first arg is a tag for the BPF program */ sys_modified.syscall_number = __NR_ppoll; sys_modified.syscall_args[0] = 1; sys_modified.syscall_args[1] = 0; sys_modified.syscall_args[2] = 0; // NULL sys_modified.syscall_args[3] = 0; // NULL umvu_poke_syscall(regs, &sys_modified, POKE_ARGS); P_SETREGS_NODIE(tid, regs); /* set the PC to execute again the original syscall */ sys_orig.prog_counter -= SYSCALL_INSTRUCTION_LEN; umvu_poke_syscall(regs, &sys_orig, POKE_ARGS); } static void transfer_tracee(pid_t newtid, syscall_arg_t clone_flags) { pthread_t newthread; pthread_attr_t thread_attr; tracer_args *t_args = (tracer_args *) malloc(sizeof(tracer_args) + vu_inheritance_inout_size()); arch_regs_struct *regs; fatal(t_args); regs = &(t_args->regs); block_tracee(newtid, regs); /*init args for new thread*/ t_args->tracee_tid = newtid; vu_inheritance_call(INH_CLONE, t_args->inherited_args, &clone_flags); /* This tracer thread won't follow (ptrace) the newtid, but delegate this task to a new tracer thread. */ P_DETACH_NODIE(newtid, 0L); pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); libc_pthread_create(&newthread, &thread_attr, &spawn_tracer, t_args); pthread_attr_destroy(&thread_attr); } /* umvu_trace code is replicated for performance issues and readability */ /* umvu_trace_legacy does not use seccomp/BPF code */ static int umvu_trace_legacy(pid_t tracee_tid) { int wstatus, sig_tid; syscall_state_t syscall_state = IN_SYSCALL; arch_regs_struct regs; struct syscall_descriptor_t syscall_desc = {.action = DOIT, .inout = NULL}; syscall_arg_t clone_flags = 0; //printk("new thread for %d\n", tracee_tid); int cloning = 0; while (1) { sig_tid = r_wait4(-1, &wstatus, __WALL | __WNOTHREAD, NULL); if (sig_tid == -1) { perror("r_wait4 -1"); vu_inheritance_call(INH_TERMINATE, NULL, NULL); nproc_update(-1); return -1; } else if (WIFSTOPPED(wstatus)) { if (WSTOPSIG(wstatus) == SIGTRAP) { if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { /* tracee is about to exit */ unsigned long exit_status; P_GETEVENTMSG(sig_tid, &exit_status); while (cloning > 0) { /* tracee_tid is terminating during cloning: wait for transfer_tracee */ sig_tid = r_wait4(-1, &wstatus, __WALL | __WNOTHREAD, NULL); if (sig_tid == -1) break; if (sig_tid != tracee_tid) { if (wstatus >> 16 == PTRACE_EVENT_STOP) { P_SYSCALL_NODIE(sig_tid, 0L); } else { transfer_tracee(sig_tid, clone_flags); cloning--; } } } P_DETACH(tracee_tid, 0L); vu_inheritance_call(INH_TERMINATE, NULL, NULL); nproc_update(-1); return exit_status; } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) { /* the tracee is doing execve() */ /* if a thread which is not the thread group leader performs * an execve() its tid become equal to the thread group leader, * we must update tracee_tid otherwise a execve could be mistaken for * a clone() */ tracee_tid = sig_tid; disable_vdso(tracee_tid); vu_inheritance_call(INH_EXEC, NULL, NULL); //printf("exec %d\n", tracee_tid); } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) || wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) { /* the tracee is doing a clone */ /* Many system calls to create processes (like fork, clone) get converted into clones */ clone_flags = syscall_desc.syscall_args[0]; cloning++; } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_VFORK << 8))) { /* vfork is an exception! */ clone_flags = CLONE_VM | CLONE_VFORK; cloning++; } P_SYSCALL(sig_tid, 0L); } else if (sig_tid != tracee_tid) { /*new tracee*/ /* The new process will starts with a PTRACE_EVENT_STOP because a P_SEIZE is used. */ if (wstatus >> 16 == PTRACE_EVENT_STOP) { P_SYSCALL_NODIE(sig_tid, 0L); } else { /* This is the first syscall of the new processes. A new tracer thread must be created to manage it.*/ transfer_tracee(sig_tid, clone_flags); cloning--; } } else if (WSTOPSIG(wstatus) == (SIGTRAP | 0x80)) { /*SYSCALL*/ if (syscall_state == IN_SYSCALL) { syscall_desc.action = DOIT; syscall_desc.waiting_pid = 0; P_GETREGS(sig_tid, ®s); umvu_peek_syscall(®s, &syscall_desc, PEEK_ARGS); syscall_handler(syscall_state, &syscall_desc); if (syscall_desc.action & UMVU_BLOCKIT) { struct syscall_descriptor_t sys_modified = syscall_desc; umvu_block(&sys_modified); umvu_poke_syscall(®s, &sys_modified, POKE_ARGS); P_SETREGS(sig_tid, ®s); } else { /* the syscall has been handled by the hypervisor. The kernel performs a useless, quick and harmless getpid */ if (syscall_desc.action & UMVU_SKIP) syscall_desc.syscall_number = __NR_getpid; if (umvu_poke_syscall(®s, &syscall_desc, POKE_ARGS)) P_SETREGS(sig_tid, ®s); } P_SYSCALL(sig_tid, 0L); if (syscall_desc.action & UMVU_CB_AFTER) { syscall_state = DURING_SYSCALL; syscall_handler(syscall_state, &syscall_desc); } syscall_state = OUT_SYSCALL; } else { /* OUT_SYSCALL */ if (syscall_desc.action != DOIT) { if (syscall_desc.waiting_pid != 0) r_kill(syscall_desc.waiting_pid, SIGKILL); P_GETREGS(sig_tid, ®s); umvu_peek_syscall(®s, &syscall_desc, PEEK_RETVALUE); if (syscall_desc.action & UMVU_CB_AFTER) syscall_handler(syscall_state, &syscall_desc); if (syscall_desc.action & UMVU_DO_IT_AGAIN) { syscall_desc.prog_counter -= SYSCALL_INSTRUCTION_LEN; umvu_poke_syscall(®s, &syscall_desc, POKE_ARGS); P_SETREGS(sig_tid, ®s); } else { syscall_desc.inout = NULL; if (umvu_poke_syscall(®s, &syscall_desc, POKE_RETVALUE)) P_SETREGS(sig_tid, ®s); } } syscall_state = IN_SYSCALL; syscall_desc.waiting_pid = 0; P_SYSCALL(sig_tid, 0L); } } else { /*group-stop or signal injection*/ P_SYSCALL(sig_tid, WSTOPSIG(wstatus)); } } else { //printk("waiting_pid? %d %d\n", sig_tid, syscall_desc.waiting_pid); if (sig_tid == syscall_desc.waiting_pid) { umvu_unblock(); syscall_desc.waiting_pid = 0; } } } } /* umvu_trace_seccomp: e more performant tracer using seccomp/BPF */ static int umvu_trace_seccomp(pid_t tracee_tid) { int wstatus, sig_tid; arch_regs_struct regs; struct syscall_descriptor_t syscall_desc = {.action = DOIT, .inout = NULL}; syscall_arg_t clone_flags = 0; int cloning = 0; //printk("new seccomp thread for %d\n", tracee_tid); while (1) { sig_tid = r_wait4(-1, &wstatus, __WALL | __WNOTHREAD, NULL); if (sig_tid == -1) { perror("r_wait4 -1"); vu_inheritance_call(INH_TERMINATE, NULL, NULL); nproc_update(-1); return -1; } else if (WIFSTOPPED(wstatus)) { if (WSTOPSIG(wstatus) == SIGTRAP) { /* PTRACE_EVENT_SECCOMP: this is the IN phase for a new syscall */ if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { if (sig_tid != tracee_tid) { /* This is the first syscall of the new processes. A new tracer thread must be created to manage it.*/ transfer_tracee(sig_tid, clone_flags); cloning--; } else { syscall_desc.action = DOIT; syscall_desc.waiting_pid = 0; P_GETREGS(sig_tid, ®s); umvu_peek_syscall(®s, &syscall_desc, PEEK_ARGS); syscall_handler(IN_SYSCALL, &syscall_desc); if (syscall_desc.action & UMVU_BLOCKIT) { struct syscall_descriptor_t sys_modified = syscall_desc; umvu_block(&sys_modified); umvu_poke_syscall(®s, &sys_modified, POKE_ARGS); P_SETREGS(sig_tid, ®s); } else if (syscall_desc.action & UMVU_SKIP) { /* SKIP_SETRETVALUE means: set syscall number to -1 -> skip the syscall */ umvu_poke_syscall(®s, &syscall_desc, SKIP_SETRETVALUE); P_SETREGS(sig_tid, ®s); } else { if (umvu_poke_syscall(®s, &syscall_desc, POKE_ARGS)) P_SETREGS(sig_tid, ®s); } if (syscall_desc.action & UMVU_CB_AFTER) { P_SYSCALL(sig_tid, 0L); syscall_handler(DURING_SYSCALL, &syscall_desc); } else P_CONT(sig_tid, 0L); } } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXIT << 8))) { /* tracee is about to exit */ unsigned long exit_status; P_GETEVENTMSG(sig_tid, &exit_status); while (cloning > 0) { /* tracee_tid is terminating during cloning: wait for transfer_tracee */ sig_tid = r_wait4(-1, &wstatus, __WALL | __WNOTHREAD, NULL); if (sig_tid == -1) break; if (sig_tid != tracee_tid) { if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { transfer_tracee(sig_tid, clone_flags); cloning--; } if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_STOP << 8))) P_CONT(sig_tid, 0L); } } P_DETACH(tracee_tid, 0L); vu_inheritance_call(INH_TERMINATE, NULL, NULL); nproc_update(-1); return exit_status; } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) { /* the tracee is doing execve() */ /* if a thread which is not the thread group leader performs * an execve() its tid become equal to the thread group leader, * we must update tracee_tid otherwise a execve could be mistaken for * a clone() */ tracee_tid = sig_tid; disable_vdso(tracee_tid); vu_inheritance_call(INH_EXEC, NULL, NULL); P_CONT(sig_tid, 0L); } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)) || wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) { /* the tracee is doing a clone */ /* many system calls to create processes (like fork, vfork, clone) get converted into clones */ clone_flags = syscall_desc.syscall_args[0]; cloning++; P_CONT(sig_tid, 0L); } else if (wstatus >> 8 == (SIGTRAP | (PTRACE_EVENT_VFORK << 8))) { /* vfork is an exception! */ clone_flags = CLONE_VM | CLONE_VFORK; cloning++; P_CONT(sig_tid, 0L); } else P_CONT(sig_tid, 0L); } else if (WSTOPSIG(wstatus) == (SIGTRAP | 0x80)) { if (syscall_desc.waiting_pid != 0) r_kill(syscall_desc.waiting_pid, SIGKILL); P_GETREGS(sig_tid, ®s); umvu_peek_syscall(®s, &syscall_desc, PEEK_RETVALUE); if (syscall_desc.action & UMVU_CB_AFTER) syscall_handler(OUT_SYSCALL, &syscall_desc); if (syscall_desc.action & UMVU_DO_IT_AGAIN) { syscall_desc.prog_counter -= SYSCALL_INSTRUCTION_LEN; umvu_poke_syscall(®s, &syscall_desc, POKE_ARGS); P_SETREGS(sig_tid, ®s); } else { if (umvu_poke_syscall(®s, &syscall_desc, POKE_RETVALUE)) P_SETREGS(sig_tid, ®s); syscall_desc.inout = NULL; } syscall_desc.waiting_pid = 0; P_CONT(sig_tid, 0L); } else { /*group-stop or signal injection*/ P_CONT(sig_tid, WSTOPSIG(wstatus)); } } else { //printk("waiting_pid? %d %d\n", sig_tid, syscall_desc.waiting_pid); if (sig_tid == syscall_desc.waiting_pid) { umvu_unblock(); syscall_desc.waiting_pid = 0; } } } } /* start the tracer */ int umvu_tracepid(pid_t childpid, syscall_handler_t syscall_handler_arg, int main) { int wstatus; if (main) nproc_update(1); /* the tracer seizes the tracee and then restarts it */ P_SEIZE(childpid, PTRACE_STD_OPTS); // P_SYSCALL if legacy, P_CONT if SECCOMP PTRACE(ptrace_next_syscall, childpid, 0L, 0L); if (syscall_handler_arg != NULL) syscall_handler = syscall_handler_arg; umvu_settid(childpid); wstatus = umvu_trace(childpid); if (main) wait4termination(); return wstatus; } /* wrapper to fork to set-up for the tracer (parent) and for the tracee root process */ int umvu_tracer_fork(int seccomp) { pid_t childpid; childpid = r_fork(); switch (childpid) { case 0: /* child */ /* wait for the tracer */ raise(SIGSTOP); /* load the seccomp filter */ if (seccomp) { if (r_prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { perror("prctl(PR_SET_NO_NEW_PRIVS)"); return -1; } if (r_seccomp(SECCOMP_SET_MODE_FILTER, 0, &seccomp_prog) == -1) { perror("when setting seccomp filter"); return -1; } } return 0; default: /*parent*/ if (seccomp) { umvu_trace = umvu_trace_seccomp; ptrace_next_syscall = PTRACE_CONT; } /* the child reached the "raise(SIGSTOP)" */ r_wait4(-1, NULL, WUNTRACED, NULL); return childpid; case -1: return -1; } } /* check if seccomp/BPF is supported */ int umvu_tracer_test_seccomp(void) { pid_t childpid; int status; struct sock_filter filter[] = { BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { .filter = filter, .len = (unsigned short) (sizeof(filter)/sizeof(filter[0])), }; childpid = r_fork(); switch (childpid) { case 0: /*child*/ if (r_prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) exit(errno); if (r_seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog) == -1) exit(errno); exit(0); default: r_wait4(childpid, &status, 0, NULL); if (WEXITSTATUS(status) != 0) { errno = WEXITSTATUS(status); return -1; } else return 0; case -1: return -1; } } __attribute__((constructor)) static void init(void) { /* init the libc_pthread_create pointer */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" libc_pthread_create = dlsym (RTLD_NEXT, "pthread_create"); #pragma GCC diagnostic pop } vuos-0.9.2/umvu/src/vu_access_emu.c000066400000000000000000000037701476575172100173140ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2020 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include static int _is_group_member(gid_t gid) { int len = getgroups(0, NULL); gid_t list[len]; int i; len = getgroups(len, list); for (i = 0; i < len; i++) { if (gid == list[i]) return 1; } return 0; } int vu_access_emu(struct vu_stat *statbuf, int mode, int flags) { if (flags & ~(AT_EACCESS | AT_SYMLINK_NOFOLLOW)) return -EINVAL; if (statbuf->st_mode == 0) return -ENOENT; if (mode == F_OK) return 0; uid_t uid = (flags & AT_EACCESS) ? geteuid() : getuid(); if (uid == 0) { // it is root if ((mode & X_OK) == 0) // RW are always allowed return 0; // X OK is X is okay for someone if (statbuf->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) return 0; } int granted; if (uid == statbuf->st_uid) // user permissions granted = (int) ((statbuf->st_mode >> 6) & mode); else { gid_t gid = (flags & AT_EACCESS) ? getegid() : getgid(); if (statbuf->st_gid == gid || _is_group_member(statbuf->st_gid)) // group permissions granted = (int) ((statbuf->st_mode >> 3) & mode); else // other permissions granted = statbuf->st_mode & mode; } if (granted == mode) return 0; return -EACCES; } vuos-0.9.2/umvu/src/vu_choice.c000066400000000000000000000207621476575172100164370ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* choice functions. A choice function chooses which module have to process a system call. The mapping between the system call type and the choice function to use is defined in vu_syscalls.conf (used to generate the array vu_syscall_table in the file syscall_table.c) When a choice function returns NULL the syscall is not processed by a service/module. It is sent to the kernel. */ #include #include #include #include #include #include #include #include #include /* The module choice depends on a pathname */ struct vuht_entry_t *choice_path(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int nested = extra->nested; struct vuht_entry_t *ht; if (extra->path == NULL) { if (extra->path_errno != 0) { sd->ret_value = -extra->path_errno; sd->action = SKIPIT; } ht = NULL; } else ht = vuht_pick(CHECKPATH, extra->path, &extra->statbuf, SET_EPOCH); printkdebug(c, "path %s: %c ht %p err = %d %s", extra->path, nested ? 'N' : '-', ht, (sd->action == SKIPIT) ? -sd->ret_value : 0, (sd->action == SKIPIT) ? "SKIPIT" : ""); if (ht) extra->mpath = vuht_path2mpath(ht, extra->path); return ht; } /* The module choice depends on a file descriptor */ struct vuht_entry_t *choice_fd(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int fd = sd->syscall_args[0]; int nested = extra->nested; struct vuht_entry_t *ht = vu_fd_get_ht(fd, nested); char path[PATH_MAX]; vu_fd_get_path(fd, nested, path, PATH_MAX); extra->path = strdup(path); extra->statbuf.st_mode = vu_fd_get_mode(fd, nested); printkdebug(c, "fd %d %s: %c ht %p", fd, extra->path, nested ? 'N' : '-', ht); if (ht) { extra->mpath = vuht_path2mpath(ht, extra->path); set_vepoch(vuht_get_vepoch(ht)); vuht_pick_again(ht); } return ht; } /* The module choice of an ioctl: * can use a file descriptor * can use the request number as a parameter */ struct vuht_entry_t *choice_ioctl(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int fd = sd->syscall_args[0]; unsigned long request = sd->syscall_args[1]; int nested = extra->nested; struct vuht_entry_t *ht = vu_fd_get_ht(fd, nested); char path[PATH_MAX]; vu_fd_get_path(fd, nested, path, PATH_MAX); extra->path = strdup(path); extra->statbuf.st_mode = vu_fd_get_mode(fd, nested); if (ht) { set_vepoch(vuht_get_vepoch(ht)); vuht_pick_again(ht); } else ht = vuht_pick(CHECKIOCTL, &request, NULL, SET_EPOCH); printkdebug(c, "ioctl %d %s: %c ht %p", fd, extra->path, nested ? 'N' : '-', ht); return ht; } /* standard choice function: if there is a path it uses the path otherwise the file descriptor */ struct vuht_entry_t *choice_std(struct syscall_descriptor_t *sd) { int syscall_number = sd->syscall_number; int patharg = vu_arch_table_patharg(syscall_number); if (patharg >= 0) return choice_path(sd); else return choice_fd(sd); } struct vuht_entry_t *choice_std_nonest(struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (nested) return NULL; else return choice_std(sd); } /* utimensat uses fd if path is NULL */ struct vuht_entry_t *choice_utimensat(struct syscall_descriptor_t *sd) { int syscall_number = sd->syscall_number; switch (syscall_number) { case __NR_utimensat: { syscall_arg_t pathaddr = sd->syscall_args[1]; if (pathaddr == (syscall_arg_t) NULL) return choice_fd(sd); else return choice_path(sd); } default: return choice_path(sd); } } /* mount uses filesystem type */ struct vuht_entry_t *choice_mount(struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (nested) /* nested calls cannot be further virtualized */ return NULL; else { struct syscall_extra_t *extra = sd->extra; struct vuht_entry_t *ht; char filesystemtype[PATH_MAX]; syscall_arg_t filesystemtype_addr = sd->syscall_args[2]; umvu_peek_str(filesystemtype_addr, filesystemtype, PATH_MAX); ht = vuht_pick(CHECKFSTYPE, filesystemtype, NULL, 0); printkdebug(c, "mount %s on %s: - ht %p", filesystemtype, extra->path, ht); return ht; } } /* mount pathname must match exactly the mountpoint */ struct vuht_entry_t *choice_umount2(struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (nested) /* nested calls cannot be further virtualized */ return NULL; else { struct syscall_extra_t *extra = sd->extra; struct vuht_entry_t *ht; if (extra->path == NULL) { if (extra->path_errno != 0) { sd->ret_value = -extra->path_errno; sd->action = SKIPIT; } ht = NULL; } else { char *no_root_path = (extra->path[1] == 0) ? "" : extra->path; ht = vuht_pick(CHECKPATHEXACT, no_root_path, &extra->statbuf, SET_EPOCH); } printkdebug(c, "umount2 %s: - ht %p err = %d %s", extra->path, ht, (sd->action == SKIPIT) ? -sd->ret_value : 0, (sd->action == SKIPIT) ? "SKIPIT" : ""); return ht; } } /* mmap uses fd (fifth argument) */ struct vuht_entry_t *choice_mmap(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int fd = sd->syscall_args[4]; int nested = extra->nested; struct vuht_entry_t *ht = vu_fd_get_ht(fd, nested); char path[PATH_MAX]; vu_fd_get_path(fd, nested, path, PATH_MAX); extra->path = strdup(path); extra->statbuf.st_mode = vu_fd_get_mode(fd, nested); printkdebug(c, "mmap2 %d %s: %c ht %p", fd, extra->path, nested ? 'N' : '-', ht); if (ht) { extra->mpath = vuht_path2mpath(ht, extra->path); set_vepoch(vuht_get_vepoch(ht)); vuht_pick_again(ht); } return ht; } /* some system calls need to use the file descriptor passed as second argument (e.g. epoll_ctl) */ struct vuht_entry_t *choice_fd2(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int fd = sd->syscall_args[2]; int nested = extra->nested; struct vuht_entry_t *ht = vu_fd_get_ht(fd, nested); char path[PATH_MAX]; vu_fd_get_path(fd, nested, path, PATH_MAX); extra->path = strdup(path); extra->statbuf.st_mode = vu_fd_get_mode(fd, nested); printkdebug(c, "fd2 %d %s: %c ht %p", fd, extra->path, nested ? 'N' : '-', ht); if (ht) { extra->mpath = vuht_path2mpath(ht, extra->path); set_vepoch(vuht_get_vepoch(ht)); vuht_pick_again(ht); } return ht; } /* socket uses the protocol family */ struct vuht_entry_t *choice_socket(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; int domain = sd->syscall_args[0]; int nested = extra->nested; struct vuht_entry_t *ht = vuht_pick(CHECKSOCKET, &domain, NULL, SET_EPOCH); printkdebug(c, "socket: fam:%d %c ht %p", domain, nested ? 'N' : '-', ht); return ht; } /* msocket uses the protocol family if the pathname is NULL, the pathname otherwise */ struct vuht_entry_t *choice_msocket(struct syscall_descriptor_t *sd) { struct syscall_extra_t *extra = sd->extra; uintptr_t path = sd->syscall_args[0]; int domain = sd->syscall_args[1]; int nested = extra->nested; if (path == 0) { /* msocket path == NULL ===> socket) */ struct vuht_entry_t *ht = vuht_pick(CHECKSOCKET, &domain, NULL, SET_EPOCH); printkdebug(c, "socket: fam:%d %c ht %p", domain, nested ? 'N' : '-', ht); return ht; } else return choice_path(sd); } /* some system calls use tier own number to chose the module */ struct vuht_entry_t *choice_sc(struct syscall_descriptor_t *sd) { int vu_syscall_number = vu_arch_table[sd->syscall_number]; struct vuht_entry_t *ht = vuht_pick(CHECKSC, &vu_syscall_number, NULL, SET_EPOCH); printkdebug(c, "sc: call:%d vcall:%d ht %p", sd->syscall_number, vu_syscall_number, ht); return ht; } __attribute__((constructor)) static void init(void) { debug_set_name(c, "CHOICE"); } vuos-0.9.2/umvu/src/vu_chroot_exec.c000066400000000000000000000123001476575172100174740ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2020 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *elf_magic = "\177ELF"; static int exec_chroot_pread(struct vuht_entry_t *ht, int fd, void *buf, size_t count, off_t offset, void *private) { if (service_getflags(ht) & VU_USE_PRW) { if (service_syscall(ht, __VU_pread64)(fd, buf, count, offset, 0, private) != ((int) count)) return -1; } else { if (service_syscall(ht, __VU_lseek)(fd, offset, SEEK_SET, private) < 0) return -1; if (service_syscall(ht, __VU_read)(fd, buf, count, private) != ((int) count)) return -1; } return 0; } void exec_chroot_rewrite_interpreter(struct vuht_entry_t *ht, struct binfmt_req_t *req) { char *e_ident = req->filehead; void *private = NULL; int fd; int i; char interpath[BINFMTBUFLEN - 1]; if (memcmp(e_ident, elf_magic, strlen(elf_magic)) != 0) return; if (e_ident[EI_DATA] != 0x01) // little endian return; if (e_ident[EI_VERSION] != 0x01) // version 1 return; vu_fs_get_rootdir(interpath, sizeof(interpath)); if (ht) { if ((fd = service_syscall(ht, __VU_open) (vuht_path2mpath(ht, req->path), O_RDONLY, 0, &private)) < 0) return; if (e_ident[EI_CLASS] == 01) { // virtual 32 Elf32_Ehdr *hdr = (Elf32_Ehdr *) req->filehead; Elf32_Phdr phdr[hdr->e_phnum]; if (exec_chroot_pread(ht, fd, phdr, sizeof(phdr), hdr->e_phoff, private) < 0) goto close_return_ht; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_INTERP) break; } if (i >= hdr->e_phnum) goto close_return_ht; int interlen = strlen(interpath); if (interlen + phdr[i].p_filesz >= sizeof(interpath)) goto close_return_ht; if (exec_chroot_pread(ht, fd, interpath + interlen, phdr[i].p_filesz, phdr[i].p_offset, private) < 0) goto close_return_ht; } else if (e_ident[EI_CLASS] == 02) { // virtual 64 Elf64_Ehdr *hdr = (Elf64_Ehdr *) req->filehead; Elf64_Phdr phdr[hdr->e_phnum]; if (exec_chroot_pread(ht, fd, phdr, sizeof(phdr), hdr->e_phoff, private) < 0) goto close_return_ht; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_INTERP) break; } if (i >= hdr->e_phnum) goto close_return_ht; int interlen = strlen(interpath); if (interlen + phdr[i].p_filesz >= sizeof(interpath)) goto close_return_ht; if (exec_chroot_pread(ht, fd, interpath + interlen, phdr[i].p_filesz, phdr[i].p_offset, private) < 0) goto close_return_ht; } else goto close_return_ht; snprintf(req->filehead, BINFMTBUFLEN + 2, "#!%s\n", interpath); close_return_ht: service_syscall(ht, __VU_close)(fd, private); } else { if ((fd = r_open(req->path, O_RDONLY)) < 0) return; if (e_ident[EI_CLASS] == 01) { // real 32 Elf32_Ehdr *hdr = (Elf32_Ehdr *) req->filehead; if (r_lseek(fd, hdr->e_phoff, SEEK_SET) < 0) goto close_return; Elf32_Phdr phdr[hdr->e_phnum]; if (r_read(fd, phdr, sizeof(phdr)) != ((int) sizeof(phdr))) goto close_return; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_INTERP) break; } if (i >= hdr->e_phnum) goto close_return; int interlen = strlen(interpath); if (interlen + phdr[i].p_filesz >= sizeof(interpath)) goto close_return; if (lseek(fd, phdr[i].p_offset, SEEK_SET) < 0) goto close_return; if (r_read(fd, interpath + interlen, phdr[i].p_filesz) != ((int) phdr[i].p_filesz)) goto close_return; } else if (e_ident[EI_CLASS] == 02) { // real 64 Elf64_Ehdr *hdr = (Elf64_Ehdr *) req->filehead; if (r_lseek(fd, hdr->e_phoff, SEEK_SET) < 0) goto close_return; Elf64_Phdr phdr[hdr->e_phnum]; if (r_read(fd, phdr, sizeof(phdr)) != ((int) sizeof(phdr))) goto close_return; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_INTERP) break; } if (i >= hdr->e_phnum) goto close_return; int interlen = strlen(interpath); if (interlen + phdr[i].p_filesz >= sizeof(interpath)) goto close_return; if (lseek(fd, phdr[i].p_offset, SEEK_SET) < 0) goto close_return; if (r_read(fd, interpath + interlen, phdr[i].p_filesz) != ((int) phdr[i].p_filesz)) goto close_return; } else goto close_return_ht; snprintf(req->filehead, BINFMTBUFLEN + 2, "#!%s\n", interpath); close_return: r_close(fd); } } vuos-0.9.2/umvu/src/vu_execute.c000066400000000000000000000134711476575172100166460ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *action_strings[] = ACTION_STRINGS; struct vuht_entry_t *choice_NULL(struct syscall_descriptor_t *sd) { return NULL; } /* default (dummy) wrappers */ void wi_NULL(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { //printk("UNMANAGED %s\n", syscallname(sd->syscall_number)); } void wd_NULL(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { } /* a dummy output wrappermust copy the return value */ void wo_NULL(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; } /* default wrapper for virtual system calls */ void vw_NULL(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { } /* set the syscall_extra_t structure fields */ static inline void set_extra ( struct syscall_extra_t *extra, struct syscall_descriptor_t *sd, char *(*getpath)(struct syscall_descriptor_t *sd, struct vu_stat *buf, uint8_t *need_rewrite)) { extra->statbuf.st_mode = 0; extra->path = getpath(sd, &extra->statbuf, &extra->path_rewrite); extra->mpath = extra->path; extra->path_errno = errno; extra->nested = VU_NOT_NESTED; extra->isexec = 0; extra->ht = NULL; extra->epoch = get_vepoch(); } /* when the syscall execution is complete: decrement the usage count of the hashtable element, and free the canonicalized path */ static inline void execute_cleanup (struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht != NULL) vuht_drop(ht); xfree(sd->extra->path); } /* vu_syscall_execute dispatches the syscall requests to the modules */ void vu_syscall_execute(syscall_state_t state, struct syscall_descriptor_t *sd) { static __thread struct syscall_extra_t extra; struct vuht_entry_t *ht; struct syscall_descriptor_t *ssd; /* execve is an exception. A successful execve does not return, the next syscall must cleanup the path and ht usage counter */ if (state == IN_SYSCALL && extra.isexec) { if (extra.ht) vuht_drop(extra.ht); xfree(extra.path); } update_vepoch(); sd->extra = &extra; ssd = set_thread_sd(sd); if (sd->syscall_number >= 0) { int sysno = vu_arch_table[sd->orig_syscall_number]; /* retrieve the entry from vu_syscall_table (see syscall_table.h) */ const struct syscall_tab_entry *tab_entry = &vu_syscall_table[sysno]; switch (state) { case IN_SYSCALL: set_extra(&extra, sd, get_syspath); printkdebug(s, "IN >>>>>> %s %s %ld", syscallname(sd->syscall_number), sd->extra->path, get_vepoch()); ht = sd->extra->ht = tab_entry->choicef(sd); if (sd->action == SKIPIT) execute_cleanup(ht,sd); else { if (vu_fs_is_chroot() || sd->extra->path_rewrite) if (sd->syscall_number != __NR_execve && sd->syscall_number != __NR_execveat) rewrite_syspath(sd, sd->extra->path); tab_entry->wrapinf(ht, sd); if ((sd->action & UMVU_CB_AFTER) == 0) execute_cleanup(ht,sd); else if (sd->syscall_number == __NR_execve || sd->syscall_number == __NR_execveat) extra.isexec = 1; } printkdebug(a,"IN %s", action_strings[sd->action % 0xf]); break; case DURING_SYSCALL: ht = sd->extra->ht; printkdebug(s, "DURING === %s %s", syscallname(sd->syscall_number), sd->extra->path); tab_entry->wrapduringf(ht, sd); printkdebug(a,"DURING %s", action_strings[sd->action % 0xf]); if ((sd->action & UMVU_CB_AFTER) == 0) execute_cleanup(ht,sd); break; case OUT_SYSCALL: ht = sd->extra->ht; printkdebug(s, "OUT <<<<<< %s %s", syscallname(sd->syscall_number), sd->extra->path); tab_entry->wrapoutf(ht, sd); execute_cleanup(ht,sd); break; } } else { /* vuos extends the set of system calls by adding some vuos specific system calls. Vuos syscalls have negative sysno. These system calls are always "virtualized" as the corresponding system calls are not provided by the kernel. */ int vsysno = - sd->syscall_number; sd->ret_value = -ENOSYS; if (vsysno < VVU_NR_SYSCALLS) { /* retrieve the entry from vvu_syscall_table (see syscall_table.h) */ const struct vsyscall_tab_entry *tab_entry = &vvu_syscall_table[vsysno]; set_extra(&extra, sd, get_vsyspath); printkdebug(s, "VIRSYSCALL ++++++ %d %s %ld", vsysno, sd->extra->path, get_vepoch()); ht = sd->extra->ht = tab_entry->choicef(sd); tab_entry->wrapf(ht, sd); execute_cleanup(ht,sd); } sd->action = SKIPIT; } set_thread_sd(ssd); } /* clone3 is not currently supported */ void wi_clone3(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->action = SKIPIT; sd->ret_value = -ENOSYS; } __attribute__((constructor)) static void init(void) { debug_set_name(s, "SYSCALL"); debug_set_name(a, "ACTION"); } vuos-0.9.2/umvu/src/vu_fd_table.c000066400000000000000000000305701476575172100167430ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* it must be power of two */ #define FD_TABLE_CHUNK 16 #define FDFLAGS_MASK FD_CLOEXEC struct vu_fd_table_t { pthread_rwlock_t lock; size_t count; // number of threads sharing this element int table_size; // size of fnode and flags arrays struct vu_fnode_t **fnode; // fd (as seen by the user proc) is the index here uint8_t *flags; // as above. the only flag handled at this level is close_on_exec }; /* global fd table for nested virtualization */ static struct vu_fd_table_t *vu_n_fd = NULL; /* thread private fd table (shared by threads created by CLONE_FILES) */ static __thread struct vu_fd_table_t *vu_fd = NULL; #define VU_FD_TABLE(nested) (nested ? vu_n_fd : vu_fd) #ifdef VU_N_FD_LOCK_DEBUG #define pthread_rwlock_rdlock(X) do { \ if (X == &vu_n_fd->lock) { \ printk("pthread_rwlock_rdlock %d\n", __LINE__); \ pthread_rwlock_rdlock(X); \ printk("pthread_rwlock_rdlock done %d\n", __LINE__); \ } \ else \ pthread_rwlock_rdlock(X); \ } while(0) #define pthread_rwlock_wrlock(X) do { \ if (X == &vu_n_fd->lock) { \ printk("pthread_rwlock_wrlock %d\n", __LINE__); \ pthread_rwlock_wrlock(X); \ printk("pthread_rwlock_wrlock done %d\n", __LINE__); \ } \ else \ pthread_rwlock_wrlock(X); \ } while(0) #define pthread_rwlock_unlock(X) do { \ if (X == &vu_n_fd->lock) { \ printk("pthread_rwlock_unlock %d\n", __LINE__); \ pthread_rwlock_unlock(X); \ printk("pthread_rwlock_unlock done %d\n", __LINE__); \ } \ else \ pthread_rwlock_unlock(X); \ } while(0) #endif static struct vu_fd_table_t *vu_fd_create(void) { struct vu_fd_table_t *newfd; newfd = malloc(sizeof(struct vu_fd_table_t)); fatal(newfd); newfd->count = 1; newfd->table_size = 0; newfd->flags = NULL; newfd->fnode = NULL; pthread_rwlock_init(&newfd->lock, NULL); return(newfd); } static void *vu_fd_clone(void *arg) { int flags = *(int *)arg; struct vu_fd_table_t *newfd; if (flags & CLONE_FILES) { /* the new thread shares the fd table */ pthread_rwlock_wrlock(&vu_fd->lock); newfd = vu_fd; newfd->count++; printkdebug(d, "clone %p->%p count=%d CLONE_FILES", vu_fd, newfd, newfd->count); pthread_rwlock_unlock(&vu_fd->lock); return newfd; } else { /* create a new fd table. update the usage count of fnode elements */ int i; newfd = malloc(sizeof(struct vu_fd_table_t)); fatal(newfd); pthread_rwlock_rdlock(&vu_fd->lock); newfd->table_size = vu_fd->table_size; newfd->fnode = malloc(newfd->table_size * sizeof(newfd->fnode[0])); fatal(newfd->fnode); newfd->flags = malloc(newfd->table_size * sizeof(newfd->flags[0])); fatal(newfd->flags); for (i = 0; i < newfd->table_size ; i++) { vu_fnode_dup(vu_fd->fnode[i]); newfd->fnode[i] = vu_fd->fnode[i]; newfd->flags[i] = vu_fd->flags[i]; } pthread_rwlock_unlock(&vu_fd->lock); pthread_rwlock_init(&newfd->lock, NULL); newfd->count=1; printkdebug(d, "clone %p->%p count=%d", vu_fd, newfd, newfd->count); } return newfd; } static void vu_fd_terminate(void) { pthread_rwlock_wrlock(&vu_fd->lock); printkdebug(d, "terminate %p count=%d", vu_fd, vu_fd->count); vu_fd->count -= 1; if (vu_fd->count == 0) { int i; struct vu_fd_table_t *old_vu_fd = vu_fd; vu_fd = NULL; pthread_rwlock_unlock(&old_vu_fd->lock); for (i = 0; i < old_vu_fd->table_size ; i++) { if (old_vu_fd->fnode[i] != NULL) vu_fnode_close(old_vu_fd->fnode[i]); } xfree(old_vu_fd->flags); xfree(old_vu_fd->fnode); pthread_rwlock_destroy(&old_vu_fd->lock); xfree(old_vu_fd); } else pthread_rwlock_unlock(&vu_fd->lock); } static void vu_fd_close_on_exec(void) { int i; pthread_rwlock_wrlock(&vu_fd->lock); for (i = 0; i < vu_fd->table_size ; i++) { if (vu_fd->fnode[i] != NULL && (vu_fd->flags[i] & FD_CLOEXEC)) { vu_fnode_close(vu_fd->fnode[i]); vu_fd->fnode[i] = NULL; vu_fd->flags[i] = 0; printkdebug(d, "close_on_exec %p fd=%d", vu_fd, i); } } pthread_rwlock_unlock(&vu_fd->lock); } static void vu_fd_table_resize(struct vu_fd_table_t *fd_table, int fd) { if (fd >= fd_table->table_size) { int i; int new_size = (fd + (FD_TABLE_CHUNK)) & ~(FD_TABLE_CHUNK - 1); fd_table->fnode = realloc(fd_table->fnode, new_size * sizeof(fd_table->fnode[0])); fatal(fd_table->fnode); fd_table->flags = realloc(fd_table->flags, new_size * sizeof(fd_table->flags[0])); fatal(fd_table->flags); for (i = fd_table->table_size; i < new_size; i++) { fd_table->fnode[i] = NULL; fd_table->flags[i] = 0; } fd_table->table_size = new_size; } } /* "connect" a fd to its fnode. The creation of a virtualized file descriptor has four steps: 1* the module opens the file (or socket) 2* create the fnode (vu_fnode_create) 3* the user level opens a "fake" file 4* connect the fd as seen by the user process to the fnode. this is *4 */ void vu_fd_set_fnode(int fd, int nested, struct vu_fnode_t *fnode, int fdflags) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); fatal(fd_table); pthread_rwlock_wrlock(&fd_table->lock); vu_fd_table_resize(fd_table, fd); fd_table->fnode[fd] = fnode; fd_table->flags[fd] = fdflags & FDFLAGS_MASK; printkdebug(d, "open %p fd=%d", vu_fd, fd); pthread_rwlock_unlock(&fd_table->lock); } int vu_fd_close(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); int ret_value; fatal(fd_table); pthread_rwlock_wrlock(&fd_table->lock); printkdebug(d, "close %p fd=%d", vu_fd, fd); if (fd >= 0 && fd < fd_table->table_size) { struct vu_fnode_t *oldfnode = fd_table->fnode[fd]; fd_table->fnode[fd] = NULL; fd_table->flags[fd] = 0; pthread_rwlock_unlock(&fd_table->lock); /* fd table must be unlocked for recursion */ if (oldfnode != NULL) ret_value = vu_fnode_close(oldfnode); else { ret_value = -1; errno = EBADF; } } else { ret_value = -1; errno = EBADF; pthread_rwlock_unlock(&fd_table->lock); } return ret_value; } void vu_fd_dup(int fd, int nested, int oldfd, int fdflags) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); fatal(fd_table); if (fd >= 0) { pthread_rwlock_wrlock(&fd_table->lock); printkdebug(d, "dup %p fd=%d->%d", vu_fd, oldfd, fd); vu_fd_table_resize(fd_table, fd); if (fd_table->fnode[fd] != NULL) vu_fnode_close(fd_table->fnode[fd]); if (oldfd >= 0 && oldfd < fd_table->table_size) { vu_fnode_dup(fd_table->fnode[oldfd]); fd_table->fnode[fd] = fd_table->fnode[oldfd]; fd_table->flags[fd] = fdflags & FDFLAGS_MASK; } else { fd_table->fnode[fd] = NULL; fd_table->flags[fd] = 0; } pthread_rwlock_unlock(&fd_table->lock); } } static struct vu_fnode_t *get_fnode_nolock(struct vu_fd_table_t *fd_table, int fd) { fatal(fd_table); if (fd >= 0 && fd < fd_table->table_size) return fd_table->fnode[fd]; else return NULL; } static uint8_t *get_flags_addr_nolock(struct vu_fd_table_t *fd_table, int fd) { fatal(fd_table); if (fd >= 0 && fd < fd_table->table_size) return &fd_table->flags[fd]; else return NULL; } struct vu_fnode_t *vu_fd_get_fnode(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); pthread_rwlock_unlock(&fd_table->lock); return fnode; } struct vuht_entry_t *vu_fd_get_ht(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; struct vuht_entry_t *ret_value; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); ret_value = fnode ? vu_fnode_get_ht(fnode) : NULL; pthread_rwlock_unlock(&fd_table->lock); return ret_value; } void vu_fd_get_path(int fd, int nested, char *dest, size_t n) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); if (dest) { struct vu_fnode_t *fnode; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); if (fnode) vu_fnode_get_path(fnode, dest, n); else dest[0] = 0; pthread_rwlock_unlock(&fd_table->lock); } } mode_t vu_fd_get_mode(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; mode_t ret_value; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); ret_value = fnode ? vu_fnode_get_mode(fnode) : 0; pthread_rwlock_unlock(&fd_table->lock); return ret_value; } int vu_fd_get_fdflags(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); uint8_t *flags_addr; int ret_value; pthread_rwlock_rdlock(&fd_table->lock); flags_addr = get_flags_addr_nolock(fd_table, fd); ret_value = flags_addr ? *flags_addr : -1; pthread_rwlock_unlock(&fd_table->lock); return ret_value; } void vu_fd_set_fdflags(int fd, int nested, int flags) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); uint8_t *flags_addr; pthread_rwlock_wrlock(&fd_table->lock); flags_addr = get_flags_addr_nolock(fd_table, fd); if (flags_addr) *flags_addr = flags & FDFLAGS_MASK; pthread_rwlock_unlock(&fd_table->lock); } int vu_fd_get_flflags(int fd, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; int ret_value; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); ret_value = fnode ? vu_fnode_get_flags(fnode) : -1; pthread_rwlock_unlock(&fd_table->lock); return ret_value; } void vu_fd_set_flflags(int fd, int nested, int flags) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); if (fnode) vu_fnode_set_flags(fnode, flags); pthread_rwlock_unlock(&fd_table->lock); } /* madules can define their own file descriptors (that may not be real file descriptors, just integers values meaningful for the module itself). This function returns the sfd correspoding to a fd */ int vu_fd_get_sfd(int fd, void **pprivate, int nested) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; int ret_value; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); ret_value = fnode ? vu_fnode_get_sfd(fnode, pprivate) : -1; pthread_rwlock_unlock(&fd_table->lock); return ret_value; } void vu_fd_get_possize_lock(int fd, int nested, off_t *pos, off_t *size) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); if (fnode) vu_fnode_get_possize_lock(fnode, pos, size); pthread_rwlock_unlock(&fd_table->lock); } void vu_fd_set_possize_unlock(int fd, int nested, off_t pos, off_t size) { struct vu_fd_table_t *fd_table = VU_FD_TABLE(nested); struct vu_fnode_t *fnode; pthread_rwlock_rdlock(&fd_table->lock); fnode = get_fnode_nolock(fd_table, fd); if (fnode) vu_fnode_set_possize_unlock(fnode, pos, size); pthread_rwlock_unlock(&fd_table->lock); } static void *vu_fd_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_CLONE: ret_value = vu_fd_clone(arg); break; case INH_START: vu_fd = ioarg; break; case INH_EXEC: vu_fd_close_on_exec(); break; case INH_TERMINATE: vu_fd_terminate(); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_n_fd = vu_fd_create(); vu_fd = vu_fd_create(); vu_inheritance_upcall_register(vu_fd_tracer_upcall); debug_set_name(d, "FD_TABLE"); } vuos-0.9.2/umvu/src/vu_file_table.c000066400000000000000000000146741476575172100173000ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #define UPDATABLE_FLAGS (O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK) /* file table element */ struct vu_fnode_t { pthread_rwlock_t lock; struct vuht_entry_t *ht; // ht entry of the module (NULL if the file is not virtualized) char *path; // absolute, canonicalized path of the file struct vu_vnode_t *vnode; // pointer to the vnode mode_t mode; // mode (including file type) int flags; // flags int count; // number of fd table entries sharing this element off_t pos; // file position for VU_USE_PRW /* module/service fields */ /* the values returned by the modules' implementations of open/socket/accept may not be real file descriptors. These values are just integers meaningful for the module itself: "service file descriptors", or sfd */ int sfd; // file descritor as known by the module void *private; // Private data for modules }; static int null_close_upcall(struct vuht_entry_t *ht, int sfd, void *private); static close_upcall_t vu_fnode_close_upcall[S_TYPES] = {S_TYPES_INIT(null_close_upcall)}; void vu_fnode_set_close_upcall(mode_t mode, close_upcall_t close_upcall) { vu_fnode_close_upcall[S_MODE2TYPE(mode)] = close_upcall; } struct vu_fnode_t *vu_fnode_create( struct vuht_entry_t *ht, const char *path, struct vu_stat *stat, int flags, int sfd, void *private) { struct vu_fnode_t *fnode = malloc(sizeof(struct vu_fnode_t)); fatal(fnode); fnode->ht = ht; fnode->path = xstrdup(path); if (stat != NULL) { fnode->vnode = vu_vnode_open(ht, stat->st_dev, stat->st_ino, stat->st_size, !!(flags & O_TRUNC)); fnode->mode = stat->st_mode; } else { fnode->vnode = NULL; fnode->mode = 0; } fnode->flags = flags; fnode->count = 1; fnode->pos = 0; fnode->sfd = sfd; fnode->private = private; printkdebug(f, "open %s (%p)", fnode->path, ht); pthread_rwlock_init(&fnode->lock, NULL); return fnode; } static int null_close_upcall(struct vuht_entry_t *ht, int sfd, void *private) { return 0; } /* close a fnode. actually it decrements the usage count and effectively closes the fnode only when the usage count is zero. vu_fnode_close_upcall provides the right function to close each type of file (socket, file, directory etc. */ int vu_fnode_close(struct vu_fnode_t *fnode) { int ret_value; pthread_rwlock_wrlock(&fnode->lock); printkdebug(f, "close %s (%p) count %d", fnode->path, fnode->ht, fnode->count); fnode->count -= 1; if (fnode->count <= 0) { struct vu_fnode_t *oldfnode = fnode; if (fnode->vnode) vu_vnode_close(fnode->vnode); xfree(fnode->path); pthread_rwlock_unlock(&fnode->lock); /* it should never fail. */ if (fnode->private == VU_FNODE_CLOSED) ret_value = 0; else ret_value = vu_fnode_close_upcall[S_MODE2TYPE(fnode->mode)](fnode->ht, fnode->sfd, fnode->private); pthread_rwlock_destroy(&fnode->lock); xfree(oldfnode); } else { pthread_rwlock_unlock(&fnode->lock); ret_value = 0; } return ret_value; } /* increment the usage count of an fnode */ void vu_fnode_dup(struct vu_fnode_t *fnode) { if (fnode != NULL) { pthread_rwlock_wrlock(&fnode->lock); fnode->count++; pthread_rwlock_unlock(&fnode->lock); } } struct vuht_entry_t *vu_fnode_get_ht(struct vu_fnode_t *v) { struct vuht_entry_t *ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = v->ht; pthread_rwlock_unlock(&v->lock); return ret_value; } void vu_fnode_get_path(struct vu_fnode_t *v, char *dest, size_t n) { *dest = 0; pthread_rwlock_rdlock(&v->lock); if (v->path != NULL) strncat(dest, v->path, n); pthread_rwlock_unlock(&v->lock); } char *vu_fnode_get_vpath(struct vu_fnode_t *v) { char *ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = vu_vnode_getvpath(v->vnode); pthread_rwlock_unlock(&v->lock); return ret_value; } mode_t vu_fnode_get_mode(struct vu_fnode_t *v) { mode_t ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = v->mode; pthread_rwlock_unlock(&v->lock); return ret_value; } int vu_fnode_get_flags(struct vu_fnode_t *v) { int ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = v->flags; pthread_rwlock_unlock(&v->lock); return ret_value; } void vu_fnode_set_flags(struct vu_fnode_t *v, int flags) { pthread_rwlock_wrlock(&v->lock); v->flags = (v->flags & ~UPDATABLE_FLAGS) | (flags & UPDATABLE_FLAGS); pthread_rwlock_unlock(&v->lock); } int vu_fnode_get_sfd(struct vu_fnode_t *v, void **pprivate) { int ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = v->sfd; if (pprivate != NULL) *pprivate = v->private; pthread_rwlock_unlock(&v->lock); return ret_value; } int vu_fnode_copyinout (struct vu_fnode_t *v, copyfun cp) { int ret_value; pthread_rwlock_rdlock(&v->lock); ret_value = vu_vnode_copyinout(v->vnode, v->path, cp); pthread_rwlock_unlock(&v->lock); return ret_value; } void vu_fnode_get_possize_lock(struct vu_fnode_t *v, off_t *pos, off_t *size) { pthread_rwlock_wrlock(&v->lock); *pos = v->pos; *size = vu_vnode_get_size_lock(v->vnode); } void vu_fnode_set_possize_unlock(struct vu_fnode_t *v, off_t pos, off_t size) { v->pos = pos; vu_vnode_set_size_unlock(v->vnode, size); pthread_rwlock_unlock(&v->lock); } off_t vu_fnode_getset_size(struct vuht_entry_t *ht, struct vu_stat *stat, off_t size) { if (stat == NULL) return -1; else return vu_vnode_getset_size(ht, stat->st_dev, stat->st_ino, size); } __attribute__((constructor)) static void init(void) { debug_set_name(f, "FILETABLE"); } vuos-0.9.2/umvu/src/vu_fnode_copy.c000066400000000000000000000067001476575172100173260ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* create a temporary copy of a virtual file */ /* some system calls need the file to be real (execve, mmap). this function creates a copy of a virtual file in tmp_path (usually in /tmp/.vu_....) */ static int copyfile_in(struct vuht_entry_t *ht, char *path, char *tmp_path) { int fdin, fdout; ssize_t n; size_t filesize = 0; char *buf[BUFSIZ]; void *private = NULL; struct vu_stat fdoutstat; //printk("COPY %s to %s\n",path,tmp_path); fdout = r_open(tmp_path, O_WRONLY | O_CREAT | O_CLOEXEC, 0700); if (fdout < 0) return -1; if (r_fstat(fdout, &fdoutstat) < 0) { r_close(fdout); return -1; } if (fdoutstat.st_size == 0) { //printk("REALCOPY %s to %s\n",path,tmp_path); fdin = service_syscall(ht, __VU_open)(vuht_path2mpath(ht, path), O_RDONLY, 0, &private); if (fdin < 0) { r_close(fdout); return -1; } if (service_getflags(ht) & VU_USE_PRW) { off_t pos = 0; while ((n = service_syscall(ht, __VU_pread64)(fdin, buf, BUFSIZ, pos, 0, private)) > 0) { ssize_t writeout = r_write(fdout, buf, n); if (writeout > 0) filesize += writeout; pos += n; } } else { while ((n = service_syscall(ht, __VU_read)(fdin, buf, BUFSIZ, private)) > 0) { ssize_t writeout = r_write(fdout, buf, n); if (writeout > 0) filesize += writeout; } } service_syscall(ht, __VU_close)(fdin, private); r_ftruncate(fdout, filesize); } r_close(fdout); return 0; } /* restore a temporary copy to a virtual file */ static int copyfile_out(struct vuht_entry_t *ht, char *path, char *tmp_path) { int fdin, fdout, n; char *buf[BUFSIZ]; void *private = NULL; fdin = r_open(path, O_RDONLY); if (fdin < 0) return -1; fdout = service_syscall(ht, __VU_open)(vuht_path2mpath(ht, path), O_RDWR, 0, &private); if (fdout < 0) { close(fdin); return -1; } if (service_getflags(ht) & VU_USE_PRW) { off_t pos = 0; while ((n = r_read(fdin, buf, BUFSIZ)) > 0) { service_syscall(ht, __VU_pwrite64)(fdout, buf, n, pos, private); pos += n; } } else { while ((n = r_read(fdin, buf, BUFSIZ)) > 0) service_syscall(ht, __VU_write)(fdout, buf, n, private); } service_syscall(ht, __VU_close)(fdout, private); r_close(fdin); return 0; } int vu_fnode_copyin(struct vu_fnode_t *fnode) { return vu_fnode_copyinout(fnode, copyfile_in); } int vu_fnode_copyout(struct vu_fnode_t *fnode) { return vu_fnode_copyinout(fnode, copyfile_out); } vuos-0.9.2/umvu/src/vu_fs.c000066400000000000000000000113721476575172100156120ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include /* file system entry */ struct vu_fs_t { pthread_rwlock_t lock; char *cwd; // current working directory char *rootdir; // current root directory (absolute canonicalized path) */ mode_t umask; // current umask size_t count; // number of threads sharing this entry int path_rewrite; // true if patnames must be rewritten }; static __thread struct vu_fs_t *vu_fs = NULL; static char *vu_fs_set_dir_nolock(char *dir, const char *s) { if (s != NULL) { char *newdir = strdup(s); if (dir != NULL) free(dir); return newdir; } else return dir; } void vu_fs_set_cwd(char *wd) { pthread_rwlock_wrlock(&vu_fs->lock); vu_fs->cwd = vu_fs_set_dir_nolock(vu_fs->cwd, wd); pthread_rwlock_unlock(&vu_fs->lock); } void vu_fs_set_rootdir(char *dir) { pthread_rwlock_wrlock(&vu_fs->lock); vu_fs->rootdir = vu_fs_set_dir_nolock(vu_fs->rootdir, dir); pthread_rwlock_unlock(&vu_fs->lock); } mode_t vu_fs_set_umask(mode_t mask) { mode_t ret_value; pthread_rwlock_wrlock(&vu_fs->lock); ret_value = vu_fs->umask; vu_fs->umask = mask & 0777; pthread_rwlock_unlock(&vu_fs->lock); return ret_value; } void vu_fs_get_rootdir(char *dest, size_t n) { *dest = 0; pthread_rwlock_rdlock(&vu_fs->lock); strncat(dest, vu_fs->rootdir, n); pthread_rwlock_unlock(&vu_fs->lock); } int vu_fs_is_chroot(void) { char ret_value; pthread_rwlock_rdlock(&vu_fs->lock); ret_value = (vu_fs->rootdir[1] != '\0'); pthread_rwlock_unlock(&vu_fs->lock); return ret_value; } void vu_fs_get_cwd(char *dest, size_t n) { *dest = 0; pthread_rwlock_rdlock(&vu_fs->lock); strncat(dest, vu_fs->cwd, n); pthread_rwlock_unlock(&vu_fs->lock); } mode_t vu_fs_get_umask(void) { mode_t ret_value; pthread_rwlock_rdlock(&vu_fs->lock); ret_value = vu_fs->umask; pthread_rwlock_unlock(&vu_fs->lock); return ret_value; } static void vu_fs_create(void) { struct vu_fs_t *newfs; newfs = malloc(sizeof(struct vu_fs_t)); fatal(newfs); newfs->cwd = get_current_dir_name(); fatal(newfs->cwd); newfs->rootdir = strdup("/"); newfs->umask = umask(0777); umask(newfs->umask); /* info on root process should never be deallocated */ newfs->count = 2; pthread_rwlock_init(&newfs->lock, NULL); vu_fs = newfs; } static void *vu_fs_clone(int flags) { struct vu_fs_t *newfs; if (flags & CLONE_FS) { pthread_rwlock_wrlock(&vu_fs->lock); newfs = vu_fs; newfs->count++; pthread_rwlock_unlock(&vu_fs->lock); return newfs; } else { newfs = malloc(sizeof(struct vu_fs_t)); fatal(newfs); pthread_rwlock_rdlock(&vu_fs->lock); newfs->cwd = strdup(vu_fs->cwd); fatal(newfs->cwd); newfs->rootdir = strdup(vu_fs->rootdir); fatal(newfs->rootdir); newfs->umask = vu_fs->umask; pthread_rwlock_unlock(&vu_fs->lock); newfs->count = 1; pthread_rwlock_init(&newfs->lock, NULL); } return newfs; } static void vu_fs_terminate(void) { pthread_rwlock_wrlock(&vu_fs->lock); vu_fs->count -= 1; if (vu_fs->count == 0) { struct vu_fs_t *old_vu_fs = vu_fs; xfree(vu_fs->cwd); xfree(vu_fs->rootdir); vu_fs = NULL; pthread_rwlock_unlock(&old_vu_fs->lock); pthread_rwlock_destroy(&old_vu_fs->lock); xfree(old_vu_fs); } else pthread_rwlock_unlock(&vu_fs->lock); } static void *vu_fs_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_CLONE: ret_value = vu_fs_clone(*(int *)arg); break; case INH_PTHREAD_CLONE: ret_value = vu_fs_clone(CLONE_FS); break; case INH_START: case INH_PTHREAD_START: vu_fs = ioarg; break; case INH_TERMINATE: case INH_PTHREAD_TERMINATE: vu_fs_terminate(); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_fs_create(); vu_inheritance_upcall_register(vu_fs_tracer_upcall); } vuos-0.9.2/umvu/src/vu_inheritance.c000066400000000000000000000037371476575172100175010ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include struct inheritance_elem_t { inheritance_upcall_t upcall; struct inheritance_elem_t *next; }; static struct inheritance_elem_t *inheritance_upcall_list_h = NULL; static struct inheritance_elem_t *inheritance_upcall_list_t = NULL; static int inheritance_upcall_list_count; void vu_inheritance_upcall_register(inheritance_upcall_t upcall) { struct inheritance_elem_t *new; new = malloc(sizeof(struct inheritance_elem_t)); fatal(new); new->upcall = upcall; new->next = NULL; if (inheritance_upcall_list_t == NULL) inheritance_upcall_list_h = new; else inheritance_upcall_list_t->next = new; inheritance_upcall_list_t = new; inheritance_upcall_list_count++; } void vu_inheritance_call(inheritance_state_t state, void **inout, void *arg) { struct inheritance_elem_t *scan; for (scan = inheritance_upcall_list_h; scan != NULL; scan = scan->next) { if (inout == NULL) (void) scan->upcall(state, NULL, arg); else { *inout = scan->upcall(state, *inout, arg); inout++; } } } size_t vu_inheritance_inout_size(void) { return inheritance_upcall_list_count * sizeof(void *); } vuos-0.9.2/umvu/src/vu_initfini.c000066400000000000000000000055411476575172100170140ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include struct voidfun_elem_t { voidfun_t upcall; struct voidfun_elem_t *next; }; /* constructor and destructor list head/tail pointers */ static struct voidfun_elem_t *constructor_list_h = NULL; static struct voidfun_elem_t *constructor_list_t = NULL; static struct voidfun_elem_t *destructor_list_h = NULL; static struct voidfun_elem_t *destructor_list_t = NULL; static struct voidfun_elem_t *umvu_voidfun_list_new(voidfun_t upcall) { struct voidfun_elem_t *new = malloc(sizeof(struct voidfun_elem_t)); fatal(new); new->upcall = upcall; new->next = NULL; return new; } void vu_constructor_register(voidfun_t upcall) { struct voidfun_elem_t *new = umvu_voidfun_list_new(upcall); if (constructor_list_t == NULL) constructor_list_h = new; else constructor_list_t->next = new; constructor_list_t = new; } void vu_destructor_register(voidfun_t upcall) { struct voidfun_elem_t *new = umvu_voidfun_list_new(upcall); if (destructor_list_t == NULL) destructor_list_h = new; else destructor_list_t->next = new; destructor_list_t = new; } static void umvu_voidfun_list_run(struct voidfun_elem_t *list) { struct voidfun_elem_t *scan; for (scan = list; scan != NULL; scan = scan->next) scan->upcall(); } static void sig_handler(int sig) { signal(sig, SIG_DFL); vu_fini(); if (sig == SIGTERM) r_exit(0); else r_kill(-getpgrp(), sig); } static void setsighandlers(void) { struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = 0, .sa_restorer = NULL}; sigfillset(&sa.sa_mask); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPOLL, SIG_IGN); signal(SIGPROF, SIG_IGN); signal(SIGVTALRM, SIG_IGN); } void vu_init(void) { setsighandlers(); umvu_voidfun_list_run(constructor_list_h); } void vu_fini(void) { umvu_voidfun_list_run(destructor_list_h); } vuos-0.9.2/umvu/src/vu_log.c000066400000000000000000000264611476575172100157700ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include static int debugfd = 2; uint64_t debugmask; __thread uint64_t tdebugmask; __thread pid_t debugtid; #define BLACK 0 #define RED 1 #define GREEN 2 #define YELLOW 3 #define BLUE 4 #define MAGENTA 5 #define CYAN 6 #define WHITE 7 struct debugcolor { unsigned int valid:1; unsigned int bright:1; unsigned int dim:1; unsigned int underscore:1; unsigned int blink:1; unsigned int reverse:1; unsigned int set_foreground:1; unsigned int set_background:1; unsigned int foreground:3; unsigned int background:3; }; static struct debugcolor debugcolor_table[64]; static const char *debug_tag_name[64]; static int console_current_level = LOG_DEFAULT; static int syslog_current_level = -1; #define SYSLOG_TOO 1 #define NO_SYSLOG 0 /* debugging output, (bypass pure_libc when loaded) */ /* printk (errors, warnings etc) can be logged on syslog. vudebug output on stderr (or user defined file) only */ static int _vprintk(const char *fmt, int syslog_switch, va_list ap) { char *s; int rv=0; int level=PRINTK_STANDARD_LEVEL; if (fmt[0] == *KERN_SOH) { /*level*/ char tag = fmt[1]; if (tag > '0') level = tag - '0'; fmt+=2; } if (level <= console_current_level) { rv=vasprintf(&s, fmt, ap); if (rv>0) rv=r_write(debugfd,s,strlen(s)); free(s); } if (syslog_switch == SYSLOG_TOO && level <= syslog_current_level) { size_t fmtlen = strlen(fmt); char fmt_no_nl[fmtlen + 1]; strcpy(fmt_no_nl, fmt); if (fmt_no_nl[fmtlen - 1] == '\n') fmt_no_nl[fmtlen - 1] = 0; if (level > LOG_DEBUG) level = LOG_DEBUG; vsyslog(level, fmt_no_nl, ap); } return rv; } int vprintk(const char *fmt, va_list ap) { return _vprintk(fmt, SYSLOG_TOO, ap); } int printk(const char *fmt, ...) { int rv; va_list ap; va_start(ap,fmt); rv = _vprintk(fmt, SYSLOG_TOO, ap); va_end(ap); return rv; } void set_console_log_level(int level) { console_current_level = level; } void set_syslog_log_level(int level) { syslog_current_level = level; } void set_log_file(char *logfile_path) { int newfd = r_open(logfile_path, O_CREAT | O_TRUNC | O_WRONLY, 0666); if (newfd < 0) printk(KERN_ERR "can't open log file %s: %s\n", newfd, strerror(errno)); else debugfd = newfd; } static const char *debug_alltags = DEBUG_ALLTAGS; /* always succeeds, returns 0 if the tag does not exist */ static int debug_tag2index(int tag) { char *tagp = strchr(debug_alltags, tag); if (tagp == NULL) return 0; else return tagp - debug_alltags; } void debug_add_tags(char *tags, int local) { for (; *tags; tags++) { int index = debug_tag2index(tags[0]); if (local) tdebugmask |= (1ULL << index); else debugmask |= (1ULL << index); } } void debug_del_tags(char *tags, int local) { for (; *tags; tags++) { int index = debug_tag2index(tags[0]); if (local) tdebugmask &= ~(1ULL << index); else debugmask &= ~(1ULL << index); } } void debug_get_tags(char *tags, size_t size, int local) { size_t index; size_t len; uint64_t mask = local ? tdebugmask : debugmask; tags[0] = '\0'; for (index = len = 1; index < DEBUG_NTAGS && len < size; index++) { if (mask & (1ULL << index)) { tags[len - 1] = debug_alltags[index]; tags[len++] = '\0'; } } } void _debug_set_name(int index, const char *s) { debug_tag_name[index] = s; } void debug_get_name(char tag, char *buf, size_t bufsize) { int index = debug_tag2index(tag); const char *name = debug_tag_name[index]; if (name == NULL) name = ""; snprintf(buf, bufsize, "%s", name); } static void _debug_set_tag_color(int tag, const char *s) { int index = debug_tag2index(tag); static struct debugcolor stdcolor; struct debugcolor color = stdcolor; for (; s && *s; s++) { switch (*s) { /* set the modifiers */ case '+': color.valid = 1; color.bright = 1; break; case '-': color.valid = 1; color.dim = 1; break; case '_': color.valid = 1; color.underscore = 1; break; case '*': color.valid = 1; color.blink = 1; break; case '#': color.valid = 1; color.reverse = 1; break; /* set the foreground color */ case 'n': color.valid = 1; color.set_foreground = 1; color.foreground = BLACK; break; case 'r': color.valid = 1; color.set_foreground = 1; color.foreground = RED; break; case 'g': color.valid = 1; color.set_foreground = 1; color.foreground = GREEN; break; case 'y': color.valid = 1; color.set_foreground = 1; color.foreground = YELLOW; break; case 'b': color.valid = 1; color.set_foreground = 1; color.foreground = BLUE; break; case 'm': color.valid = 1; color.set_foreground = 1; color.foreground = MAGENTA; break; case 'c': color.valid = 1; color.set_foreground = 1; color.foreground = CYAN; break; case 'w': color.valid = 1; color.set_foreground = 1; color.foreground = WHITE; break; /* set the background color */ case 'N': color.valid = 1; color.set_background = 1; color.background = BLACK; break; case 'R': color.valid = 1; color.set_background = 1; color.background = RED; break; case 'G': color.valid = 1; color.set_background = 1; color.background = GREEN; break; case 'Y': color.valid = 1; color.set_background = 1; color.background = YELLOW; break; case 'B': color.valid = 1; color.set_background = 1; color.background = BLUE; break; case 'M': color.valid = 1; color.set_background = 1; color.background = MAGENTA; break; case 'C': color.valid = 1; color.set_background = 1; color.background = CYAN; break; case 'W': color.valid = 1; color.set_background = 1; color.background = WHITE; break; } } debugcolor_table[index] = color; } void debug_set_color(char *tags, const char *s) { for (; *tags; tags++) _debug_set_tag_color(tags[0], s); } void debug_set_color_string(const char *s) { char sc[strlen(s) + 1]; char *sx, *tags, *tmp; strcpy(sc, s); for (sx = sc; (tags = strtok_r(sx, " ", &tmp)) != NULL; sx = NULL) { char *colstring = strchr(tags, ':'); if (colstring) { *colstring++ = '\0'; debug_set_color(tags, colstring); } else debug_set_color(tags, ""); } } static int color_esc_sequence_len(struct debugcolor color) { int len = 3; /*ESC... m\0" */ if (color.bright) len += 2; if (color.dim) len += 2; if (color.underscore) len += 2; if (color.blink) len += 2; if (color.reverse) len += 2; if (color.set_foreground) len += 3; if (color.set_background) len += 3; return len; } static void generate_color_esc_sequence(struct debugcolor color, char *seq) { #if 0 printf(" %d %d %d %d %d %d %d %d %d %d\n", color.valid, color.bright, color.dim, color.underscore, color.blink, color.reverse, color.set_foreground, color.set_background, color.foreground, color.background); #endif char sep = '['; *seq++ = '\033'; if (color.bright) { *seq++ = sep; *seq++ = '1'; sep = ';'; } if (color.dim) { *seq++ = sep; *seq++ = '2'; sep = ';'; } if (color.underscore) { *seq++ = sep; *seq++ = '4'; sep = ';'; } if (color.blink) { *seq++ = sep; *seq++ = '5'; sep = ';'; } if (color.reverse) { *seq++ = sep; *seq++ = '7'; sep = ';'; } if (color.set_foreground) { *seq++ = sep; *seq++ = '3'; *seq++ = '0' + color.foreground; sep = ';'; } if (color.set_background) { *seq++ = sep; *seq++ = '4'; *seq++ = '0' + color.background; sep = ';'; } *seq++ = 'm'; *seq = 0; } static const char *esc_reset_color_sequence = "\033[0m"; int _printkdebug(int index, const char *fmt, ...) { int rv; struct debugcolor color = debugcolor_table[index]; va_list ap; int save_errno = errno; int fmtlen = strlen(fmt); int prefixlen = strcspn(fmt, " \n"); const char *tag_string = debug_tag_name[index] ? debug_tag_name[index] : ""; const char *tag_string_sep = *tag_string ? " " : ""; int tagstringlen = strlen(tag_string) + strlen(tag_string_sep); int postfixlen = fmtlen - prefixlen; va_start(ap, fmt); if (color.valid && isatty(debugfd)) { /* color printing is supported and requested */ int seqlen = color_esc_sequence_len(color); int newfmtlen = fmtlen + seqlen + sizeof(esc_reset_color_sequence) + tagstringlen + 2; char newfmt[newfmtlen]; char color_esc_sequence[seqlen]; postfixlen = fmtlen - prefixlen; generate_color_esc_sequence(color, color_esc_sequence); /* newfmt is a copy of fmt with the tag name added at the first space + heading and trailing escape sequences to display the message in the requested color */ /* printkdebug(a, "log message with %s %d etc", string, number) becomes for the definition in vu_log.h: _printkdebug(33, "%s:%d log message with %s %d etc\n", basename(__FILE__), __LINE__, string, number) newfmt is: "(color_set_seq)%s:%d NAME_OF_A log message with %s %d etc(color_reset_seq)\n" where (color_set_sequence) and (color_reset_seq) are escapesequence to change the font color. and then newfmt is the format for _vprintk using the va_list ap (containing all the parameters)/ */ snprintf(newfmt, newfmtlen, "%s%.*s%s%s%.*s%s", color_esc_sequence, prefixlen, fmt, tag_string_sep, tag_string, postfixlen, fmt+prefixlen, esc_reset_color_sequence); rv = _vprintk(newfmt, NO_SYSLOG, ap); } else { /* monochrome printing */ int newfmtlen = fmtlen + tagstringlen + 2; char newfmt[newfmtlen]; /* newfmt is a copy of fmt with the tag name added at the first space */ /* printkdebug(a, "log message with %s %d etc", string, number) becomes for the definition in vu_log.h: _printkdebug(33, "%s:%d log message with %s %d etc\n", basename(__FILE__), __LINE__, string, number) newfmt is: "%s:%d NAME_OF_A log message with %s %d etc\n" and then newfmt is the format for _vprintk using the va_list ap (containing all the parameters)/ */ snprintf(newfmt, newfmtlen, "%.*s%s%s%.*s", prefixlen, fmt, // %.*s copy prefix (minlen=prefixlen) tag_string_sep, tag_string, // tag (with a space if tag_string is not NULL) postfixlen, fmt+prefixlen); rv = _vprintk(newfmt, NO_SYSLOG, ap); } va_end(ap); errno = save_errno; return rv; } void printkdump(void *buf, int count) { unsigned char *v = buf; int i; for (i=0; i, Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include /* mmap-ped area: address, length, fnode and offset in the file */ struct vu_mmap_area_t { uintptr_t addr; size_t length; struct vu_fnode_t *fnode; off_t offset; struct vu_mmap_area_t *next; }; /* user processes/threads sharing the same memory view, share the mmap table element in VUOS. count is the number of processes/threads using the map and area_list_head is the list of mmapped areas, in increasing address sorting */ struct vu_mmap_t { pthread_rwlock_t lock; size_t count; struct vu_mmap_area_t *area_list_head; }; /* Each guardian angel has here the pointer to its mmap table element */ static __thread struct vu_mmap_t *vu_mmap = NULL; /* add a new mmapped area */ void vu_mmap_mmap(uintptr_t addr, size_t length, struct vu_fnode_t *fnode, off_t offset) { struct vu_mmap_area_t *new = malloc(sizeof(*new)); struct vu_mmap_area_t **scan; fatal(new); new->addr = addr; new->length = length; new->fnode = fnode; new->offset = offset; fatal(vu_mmap); /* find the inserting position */ for (scan = &vu_mmap->area_list_head; *scan != NULL && (*scan)->addr < addr; scan = &((*scan)->next)) ; /* each mmap area increases the counterof vnode usage */ vu_fnode_dup(fnode); new->next = *scan; *scan = new; } /* delete a mmapped area */ void vu_mmap_munmap(uintptr_t addr, size_t length) { struct vu_mmap_area_t **scan; struct vu_mmap_area_t **next; /* it seems that the deallocation of pthreads' stacks happens after vu_mmap_terminate */ if (vu_mmap == NULL) return; //fatal(vu_mmap); for (scan = &vu_mmap->area_list_head; *scan != NULL; scan = next) { struct vu_mmap_area_t *this = *scan; next = &((*scan)->next); /* area interval: +----+ unmap interval: +----+ go ahead to the next element */ if (addr + length <= this->addr) continue; /* area interval: +----+ unmap interval: +----+ no more matches are possible */ if (this->addr + this->length <= addr) break; if (addr <= this->addr) { if (addr + length >= this->addr + this->length) { /* case 1: area interval: +-----------+ unmap interval: +--------------------------------------+ including the case area interval: +---------+ unmap interval: +---------+ this area element iscompletely unmapped: decrement vnote usage count */ *scan = this->next; vu_fnode_close(this->fnode); free(this); next = scan; } else { /* case 2: area interval: +-----------+ unmap interval: +-------+ new area: +---+ same number of areas */ /* partial unmapping (heading) */ /* unload this->addr, addr + length - this->addr */ this->length = this->addr + this->length - (addr + length); this->offset += addr + length - this->addr; this->addr = addr; break; } } else { if (addr + length >= this->addr + this->length) { /* case 3: area interval: +-----------+ unmap interval: +-------+ new area: +---+ same number of areas */ /* partial unmapping (trailing)*/ /* unload addr, this->addr + this->length - addr */ this->length = addr + length - this->addr; } else { /* case 4: area interval: +-----------+ unmap interval: +---+ new areas: +---+ +---+ one more area element: increment vnote usage count */ /* partial **nested** interval */ /* unload addr, length */ struct vu_mmap_area_t *new = malloc(sizeof(*new)); fatal(new); new->addr = this->addr; new->length = addr - this->addr; new->fnode = this->fnode; new->offset = this->offset; new->next = this; *scan = new; this->length = this->addr + this->length - (addr + length); this->offset += addr + length - this->addr; this->addr = addr + length; vu_fnode_dup(this->fnode); break; } } } } /* remap a mmap interval */ void vu_mmap_mremap(uintptr_t addr, size_t length, uintptr_t newaddr, size_t newlength) { struct vu_mmap_area_t **scan; struct vu_mmap_area_t *this; fatal(vu_mmap); /* search the old position */ for (scan = &vu_mmap->area_list_head; *scan != NULL && (*scan)->addr < addr; scan = &((*scan)->next)) ; this = *scan; if (this && this->addr == addr && this->length == length) { /* found! extract from the list */ *scan = this->next; /* change new addr and length */ this->addr = newaddr; this->length = newlength; /* reinsert the element in its new position */ for (scan = &vu_mmap->area_list_head; *scan != NULL && (*scan)->addr < newaddr; scan = &((*scan)->next)) ; this->next = *scan; *scan = this; } } /* manage inheritance of mmap tables */ static void vu_mmap_create(void) { struct vu_mmap_t *newmmap; newmmap = malloc(sizeof(struct vu_mmap_t)); fatal(newmmap); newmmap->count = 1; newmmap->area_list_head = NULL; pthread_rwlock_init(&newmmap->lock, NULL); vu_mmap = newmmap; } static void *vu_mmap_clone(void *arg) { int flags = *(int *)arg; struct vu_mmap_t *newmmap; if (flags & CLONE_VM) { pthread_rwlock_wrlock(&vu_mmap->lock); newmmap = vu_mmap; newmmap->count++; pthread_rwlock_unlock(&vu_mmap->lock); return newmmap; } else { newmmap = malloc(sizeof(struct vu_mmap_t)); fatal(newmmap); newmmap->count = 1; newmmap->area_list_head = NULL; pthread_rwlock_init(&newmmap->lock, NULL); } return newmmap; } static void vu_mmap_terminate(void) { pthread_rwlock_wrlock(&vu_mmap->lock); vu_mmap->count -= 1; if (vu_mmap->count == 0) { struct vu_mmap_t *old_vu_mmap = vu_mmap; /* sync and close all the mmapped areas */ vu_mmap_munmap(0, (size_t) -1); vu_mmap = NULL; pthread_rwlock_unlock(&old_vu_mmap->lock); pthread_rwlock_destroy(&old_vu_mmap->lock); xfree(old_vu_mmap); } else pthread_rwlock_unlock(&vu_mmap->lock); } static void vu_mmap_exec(void) { vu_mmap_terminate(); vu_mmap_create(); } static void *vu_mmap_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_CLONE: ret_value = vu_mmap_clone(arg); break; case INH_START: vu_mmap = ioarg; break; case INH_EXEC: vu_mmap_exec(); break; case INH_TERMINATE: vu_mmap_terminate(); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_mmap_create(); vu_inheritance_upcall_register(vu_mmap_tracer_upcall); } vuos-0.9.2/umvu/src/vu_mod_inheritance.c000066400000000000000000000073761476575172100203430ustar00rootroot00000000000000#include #include #include #include #include #include /* Thread safe implementation of the functions defined in vu_inheritance.h. These functions ahve been designed for modules aiming to define and maintain per thread data stuctures */ struct mod_inheritance_elem_t { mod_inheritance_upcall_t upcall; struct mod_inheritance_elem_t *next; }; static pthread_rwlock_t mod_inheritance_upcall_rwlock = PTHREAD_RWLOCK_INITIALIZER; static struct mod_inheritance_elem_t *mod_inheritance_upcall_list_h = NULL; static int mod_inheritance_upcall_list_count; static __thread struct mod_inheritance_exec_arg mod_exec_arg = {-1, -1}; /* setuid/setgid are passed to modules: mod_exec_arg is an arg of MOD_INH_EXEC */ void vu_exec_setuid(uid_t uid) { mod_exec_arg.exec_uid = uid; } void vu_exec_setgid(gid_t gid) { mod_exec_arg.exec_gid = gid; } void mod_inheritance_upcall_register(mod_inheritance_upcall_t upcall) { struct mod_inheritance_elem_t **scan; pthread_rwlock_wrlock(&mod_inheritance_upcall_rwlock); for (scan = &mod_inheritance_upcall_list_h; *scan != NULL; scan = &((*scan) -> next)) ; *scan = malloc(sizeof(struct mod_inheritance_elem_t)); fatal(*scan); (*scan)->upcall = upcall; (*scan)->next = NULL; mod_inheritance_upcall_list_count++; pthread_rwlock_unlock(&mod_inheritance_upcall_rwlock); } void mod_inheritance_upcall_deregister(mod_inheritance_upcall_t upcall) { struct mod_inheritance_elem_t **scan; pthread_rwlock_wrlock(&mod_inheritance_upcall_rwlock); for (scan = &mod_inheritance_upcall_list_h; *scan != NULL; scan = &((*scan) -> next)) { struct mod_inheritance_elem_t *this = *scan; if (this->upcall == upcall) { *scan = this->next; mod_inheritance_upcall_list_count--; xfree(this); break; } } pthread_rwlock_unlock(&mod_inheritance_upcall_rwlock); } static void mod_inheritance_call(mod_inheritance_state_t state, void **inout, void *arg) { struct mod_inheritance_elem_t *scan; for (scan = mod_inheritance_upcall_list_h; scan != NULL; scan = scan->next) { if (inout == NULL) (void) scan->upcall(state, NULL, arg); else { *inout = scan->upcall(state, *inout, arg); inout++; } } } static void *vu_mod_inh_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; void **args; /* CLONE/START protection against mod_inheritance_upcall_list_count modifications: INH_CLONE uses "passing le baton" and keeps the RDLOCK pending until INH_START */ switch (state) { case INH_CLONE: pthread_rwlock_rdlock(&mod_inheritance_upcall_rwlock); if (mod_inheritance_upcall_list_count > 0) { args = malloc(mod_inheritance_upcall_list_count * sizeof(void *)); fatal(args); mod_inheritance_call(MOD_INH_CLONE, args, arg); ret_value = args; } break; case INH_START: if (mod_inheritance_upcall_list_count > 0) { args = (void **) ioarg; mod_inheritance_call(MOD_INH_START, args, NULL); xfree(args); } pthread_rwlock_unlock(&mod_inheritance_upcall_rwlock); break; case INH_EXEC: pthread_rwlock_rdlock(&mod_inheritance_upcall_rwlock); if (mod_inheritance_upcall_list_count > 0) { mod_inheritance_call(MOD_INH_EXEC, NULL, &mod_exec_arg); mod_exec_arg.exec_uid = -1; mod_exec_arg.exec_gid = -1; } pthread_rwlock_unlock(&mod_inheritance_upcall_rwlock); break; case INH_TERMINATE: pthread_rwlock_rdlock(&mod_inheritance_upcall_rwlock); if (mod_inheritance_upcall_list_count > 0) mod_inheritance_call(MOD_INH_TERMINATE, NULL, NULL); pthread_rwlock_unlock(&mod_inheritance_upcall_rwlock); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_inheritance_upcall_register(vu_mod_inh_tracer_upcall); } vuos-0.9.2/umvu/src/vu_modutils.c000066400000000000000000000137531476575172100170470ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include typedef void (* voidfun)(void); long sys_enosys(void) { errno = ENOSYS; return -1; } /* This will be prefixed with getent("$HOME") */ #define USER_MODULES_PATH "/.vu/modules" #define MODULES_EXT ".so" #ifndef MAX #define MAX(a, b) ((a > b) ? a : b) #endif static inline char *gethomedir(void) { char *homedir = getenv("HOME"); /* If there is no home directory, use CWD */ if (!homedir) homedir = "."; return homedir; } /* * Try to dlopen a module (o submodule) trying different names and locations: * * 1) dlopen(modname) * 2) dlopen(modname.so) * 3) dlopen(user_vu_plugin_directory/modname) * 4) dlopen(user_vu_plugin_directory/modname.so) * 5) dlopen(global_vu_plugin_directory/modname) * 6) dlopen(global_vu_plugin_directory/modname.so) * */ static void *module_dlopen(const char *modname, int flags) { #define TRY_DLOPEN(...) \ { \ snprintf(testpath, tplen, __VA_ARGS__); \ if ((handle = dlopen(testpath, flags))) { \ return handle; \ } \ } void *handle; char *homedir = gethomedir(); int tplen = strlen(modname) + strlen(MODULES_EXT) + 2 + // + 1 is for a '/' and + 1 for \0 MAX(strlen(MODULES_INSTALL_PATH), strlen(homedir) + strlen(USER_MODULES_PATH)); char testpath[tplen]; if (!modname) return NULL; if ((handle = dlopen(modname, flags))) return handle; TRY_DLOPEN("%s%s/%s", homedir, USER_MODULES_PATH, modname); TRY_DLOPEN("%s%s/%s%s", homedir, USER_MODULES_PATH, modname, MODULES_EXT); TRY_DLOPEN("%s/%s", MODULES_INSTALL_PATH, modname); TRY_DLOPEN("%s/%s%s", MODULES_INSTALL_PATH, modname, MODULES_EXT); return NULL; #undef TRY_DLOPEN } /* utility function to load sub-modules. currently it is a forwarding function to module_dlopen. it has been defined as a specific function to permit customization in the future. */ void *vu_mod_dlopen(const char *modname, int flags) { return module_dlopen(modname, flags); } struct vu_service_t *module_load(const char *modname) { void *handle; struct vu_module_t *module; if (!(handle = module_dlopen(modname, RTLD_LAZY | RTLD_GLOBAL))) { errno = ENOENT; return NULL; } /* populate vu_service_t structure, so that the hypervisor can access and call the function implemented by the module. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" if ((module = dlsym(handle, "vu_module"))) { #pragma GCC diagnostic pop struct vu_service_t *service = malloc(sizeof(struct vu_service_t) + VU_NR_SYSCALLS * sizeof(syscall_t)); int prefixlen = strlen(module->name) + 4; int fnamelen = prefixlen + VU_SYSCALL_MAX_NAMELEN + 1; char fname[fnamelen]; int i; fatal(service); printkdebug(m, "Loading %s", module->name); service->mod = module; service->dlhandle = handle; service->service_ht = NULL; service->private = NULL; snprintf(fname, fnamelen, "vu_%s_",module->name); for (i = 0; i < VU_NR_MODULE_SYSCALLS; i++) { strcpy(fname+prefixlen, vu_syscall_names[i]); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" service->module_syscall[i] = dlsym(handle, fname); #pragma GCC diagnostic pop if (service->module_syscall[i] == NULL) { service->module_syscall[i] = sys_enosys; } else { printkdebug(m, "%s syscall %s -> %s", module->name, vu_syscall_names[i], fname); } } return service; } else { errno = EINVAL; return NULL; } } syscall_t *vu_syscall_handler_pointer(struct vu_service_t *service, char *name) { int i; static syscall_t useless; for (i = 0; i < VU_NR_MODULE_SYSCALLS; i++) { if (strcmp(name, vu_syscall_names[i]) == 0) return &service->module_syscall[i]; } useless = NULL; return &useless; } void module_unload(struct vu_service_t *service) { fatal(service); printkdebug(m, "Unloading %s", service->mod->name); dlclose(service->dlhandle); xfree(service); } voidfun *module_getsym(struct vu_service_t *service, char *symbol) { char symnamelen = strlen(service->mod->name) + strlen(symbol) + 5; char symname[symnamelen]; snprintf(symname, symnamelen, "vu_%s_%s",service->mod->name, symbol); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" return dlsym(service->dlhandle, symname); #pragma GCC diagnostic pop } void module_run_init(struct vu_service_t *service) { void * (*init)(void); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" init = module_getsym(service, "init"); #pragma GCC diagnostic pop if (init) { printkdebug(m, "%s running vu_%s_init", service->mod->name, service->mod->name); service->private = init(); } } int module_run_fini(struct vu_service_t *service) { int (*fini)(void *); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" fini = module_getsym(service, "fini"); #pragma GCC diagnostic pop if (fini) { printkdebug(m, "%s running vu_%s_fini", service->mod->name, service->mod->name); return fini(service->private); } else return 0; } __attribute__((constructor)) static void init(void) { debug_set_name(m, "MODULE"); } vuos-0.9.2/umvu/src/vu_name.c000066400000000000000000000026371476575172100161260ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include static pthread_mutex_t vu_name_mutex = PTHREAD_MUTEX_INITIALIZER; static char vu_name[_UTSNAME_LENGTH]; void set_vu_name(char *name) { pthread_mutex_lock(&vu_name_mutex); vu_name[_UTSNAME_LENGTH - 1 ] = 0; strncpy(vu_name, name, _UTSNAME_LENGTH - 1); pthread_mutex_unlock(&vu_name_mutex); } void get_vu_name(char *name, size_t len) { if (len > _UTSNAME_LENGTH) len = _UTSNAME_LENGTH; pthread_mutex_lock(&vu_name_mutex); memcpy(name, vu_name, len); pthread_mutex_unlock(&vu_name_mutex); } vuos-0.9.2/umvu/src/vu_nesting.c000066400000000000000000000127751476575172100166610ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* Nested virtualization support through module self-virtualization. This source code uses libpurelibc */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PURELIBC_LIB "libpurelibc.so" /* self-virtualization syscall request capturing function. */ /* wrappers for nested vritualization are the same used by vu_execute.c */ static long int capture_nested_syscall(long int syscall_number, ...) { va_list ap; struct syscall_extra_t extra; struct syscall_descriptor_t sd = {.extra = &extra, .inout = NULL}; struct vuht_entry_t *ht; int sysno = vu_arch_table[syscall_number]; const struct syscall_tab_entry *tab_entry = &vu_syscall_table[sysno]; long int ret_value; struct syscall_descriptor_t *ssd = set_thread_sd(&sd); epoch_t e = get_vepoch(); sd.orig_syscall_number = sd.syscall_number = syscall_number; va_start (ap, syscall_number); sd.syscall_args[0]=va_arg(ap,long int); sd.syscall_args[1]=va_arg(ap,long int); sd.syscall_args[2]=va_arg(ap,long int); sd.syscall_args[3]=va_arg(ap,long int); sd.syscall_args[4]=va_arg(ap,long int); sd.syscall_args[5]=va_arg(ap,long int); va_end(ap); sd.action = DOIT; sd.ret_value = 0; extra.statbuf.st_mode = 0; extra.path = get_nested_syspath(syscall_number, sd.syscall_args, &extra.statbuf, &extra.path_rewrite); extra.mpath = extra.path; extra.path_errno = errno; extra.nested = VU_NESTED; extra.isexec = 0; extra.ht = NULL; extra.epoch = get_vepoch(); printkdebug(n, "IN >>>>>> %s %s %d epoch %lx", syscallname(sd.syscall_number), extra.path, errno, e); ht = extra.ht = tab_entry->choicef(&sd); if (sd.action != SKIPIT) tab_entry->wrapinf(ht, &sd); if (sd.action != SKIPIT) { long orig_ret_value = native_syscall(syscall_number, sd.syscall_args[0], sd.syscall_args[1], sd.syscall_args[2], sd.syscall_args[3], sd.syscall_args[4], sd.syscall_args[5]); sd.orig_ret_value = (orig_ret_value == -1) ? -errno : orig_ret_value; if (sd.action == DOIT_CB_AFTER) tab_entry->wrapoutf(ht, &sd); else sd.ret_value = sd.orig_ret_value; sd.inout = NULL; } ret_value = sd.ret_value; if (ht != NULL) vuht_drop(ht); xfree(extra.path); set_thread_sd(ssd); set_vepoch(e); if (ret_value < 0) { errno = -ret_value; ret_value = -1; } return ret_value; } static long int capture_forward_syscall(long int syscall_number, ...) { syscall_arg_t syscall_args[SYSCALL_ARG_NR]; va_list ap; va_start (ap, syscall_number); syscall_args[0]=va_arg(ap,long int); syscall_args[1]=va_arg(ap,long int); syscall_args[2]=va_arg(ap,long int); syscall_args[3]=va_arg(ap,long int); syscall_args[4]=va_arg(ap,long int); syscall_args[5]=va_arg(ap,long int); va_end(ap); //printk("capture_forward_syscall %d\n", syscall_number); return native_syscall(syscall_number, syscall_args[0], syscall_args[1], syscall_args[2], syscall_args[3], syscall_args[4], syscall_args[5]); } typedef long (*sfun)(); void vu_nesting_disable(void) { sfun (*_pure_start_p)(); char *ld_preload = getenv("LD_PRELOAD"); //printk("NESTINGDISABLE %d\n", gettid()); if (ld_preload != NULL && strcmp(ld_preload, PURELIBC_LIB) == 0) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" _pure_start_p = dlsym(RTLD_DEFAULT,"_pure_start"); #pragma GCC diagnostic pop if (_pure_start_p) native_syscall = _pure_start_p(capture_forward_syscall, 0); } unsetenv("LD_PRELOAD"); } void vu_nesting_enable(void) { sfun (*_pure_start_p)(); char *ld_preload = getenv("LD_PRELOAD"); //printk("NESTINGENABLE %d\n", gettid()); if (ld_preload != NULL && strcmp(ld_preload, PURELIBC_LIB) == 0) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" _pure_start_p = dlsym(RTLD_DEFAULT,"_pure_start"); #pragma GCC diagnostic pop if (_pure_start_p) { printk(KERN_INFO "Purelibc found: nested virtualization enabled\n"); native_syscall = _pure_start_p(capture_nested_syscall, 0); } } } void vu_nesting_init(int argc, char **argv) { char *ld_preload = getenv("LD_PRELOAD"); /* continue if purelibc is loaded, otherwise add LD_PRELOAD to the environment and reload the hypervisor by execv("/proc/self/exe", argv); */ if (ld_preload == NULL || strcmp(ld_preload, PURELIBC_LIB) != 0) { if (setenv("LD_PRELOAD", PURELIBC_LIB, 1) == 0) { execv("/proc/self/exe", argv); } printk(KERN_ERR "Purelibc cannot be loaded, option disabled\n"); } } __attribute__((constructor)) static void init(void) { debug_set_name(n, "NESTED"); } vuos-0.9.2/umvu/src/vu_pthreads.c000066400000000000000000000044241476575172100170140ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include static int (*libc_pthread_create)(); struct _pthread_arg { void *(*start_routine) (void *); void *start_arg; void *inherited_args[]; }; void cleanup(void *arg) { //printk("cleanup \n"); vu_inheritance_call(INH_PTHREAD_TERMINATE, NULL, NULL); } void *_pthread_wrapper(void *arg) { struct _pthread_arg *ptarg = arg; void *(*start_routine) (void *) = ptarg->start_routine; void *start_arg = ptarg->start_arg; vu_inheritance_call(INH_PTHREAD_START, ptarg->inherited_args, NULL); free(ptarg); pthread_cleanup_push(cleanup, NULL); //printk("start_routine \n"); start_arg = start_routine(start_arg); //printk("start_routine DONE\n"); pthread_cleanup_pop(1); return start_arg; } int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) { //printk("pthread_create\n"); struct _pthread_arg *ptarg = malloc(sizeof(struct _pthread_arg) + vu_inheritance_inout_size()); fatal(ptarg); ptarg->start_routine = start_routine; ptarg->start_arg = arg; vu_inheritance_call(INH_PTHREAD_CLONE, ptarg->inherited_args, (void *) -1); return libc_pthread_create(thread, attr, _pthread_wrapper, ptarg); } __attribute__((constructor)) static void init(void) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" libc_pthread_create = dlsym (RTLD_NEXT, "pthread_create"); #pragma GCC diagnostic pop } vuos-0.9.2/umvu/src/vu_pushpop.c000066400000000000000000000025771476575172100167070ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include /* push and pop data on the user process stack */ #define __WORDMASK ((__WORDSIZE / 8) - 1) #define WORDALIGN(X) (((X) + __WORDMASK) & ~__WORDMASK) syscall_arg_t vu_push(struct syscall_descriptor_t *sd, void *buf, size_t datalen) { sd->stack_pointer -= WORDALIGN(datalen); umvu_poke_data(sd->stack_pointer, buf, datalen); return sd->stack_pointer; } void vu_pop(struct syscall_descriptor_t *sd, void *buf, size_t datalen) { umvu_peek_data(sd->stack_pointer, buf, datalen); sd->stack_pointer += WORDALIGN(datalen); } vuos-0.9.2/umvu/src/vu_slow_calls.c000066400000000000000000000063171476575172100173470ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STACKSIZE 4096 struct slowcall { int epfd; //char stack[STACKSIZE]; }; static int (*libc_clone)(); int vu_slowcall_test(struct slowcall *sc) { struct pollfd pfd = {sc->epfd, POLLIN, 0}; return poll(&pfd, 1, 0); } struct slowcall *vu_slowcall_in(struct vuht_entry_t *ht, int fd, uint32_t events, int nested) { if (vu_fd_get_flflags(fd, nested) & O_NONBLOCK) return NULL; else { void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int epfd = r_epoll_create1(EPOLL_CLOEXEC); struct epoll_event event = {.events = events, .data.fd = fd}; int ret_value = service_syscall(ht, __VU_epoll_ctl)(epfd, EPOLL_CTL_ADD, sfd, &event, private); //printk("vu_slowcall_in... %d (add %d)\n", epfd, ret_value); if (ret_value < 0) { r_close(epfd); return NULL; } else { struct slowcall *sc = malloc(sizeof(struct slowcall)); sc->epfd = epfd; return sc; } } } static int slow_thread(void *arg) { struct slowcall *sc = arg; struct pollfd pfd = {sc->epfd, POLLIN, 0}; //printk("vu_slowcall_during... %d\n", pfd); poll(&pfd, 1, -1); //printk("vu_slowcall_wakeup %d %d\n", errno); return 0; } pid_t vu_slowcall_during(struct slowcall *sc) { //printk(">>>>>>>>>%lu\n", pthread_self()); #if 0 return libc_clone(slow_thread, sc->stack + STACKSIZE, CLONE_FILES | CLONE_VM | CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD, sc); #endif pid_t pid; if ((pid = r_fork()) == 0) r_exit(slow_thread(sc)); return pid; } void vu_slowcall_out(struct slowcall *sc, struct vuht_entry_t *ht, int fd, uint32_t events, int nested) { void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); struct epoll_event event = {.events = events, .data.fd = fd}; //printk("vu_slowcall_wakeup...\n"); service_syscall(ht, __VU_epoll_ctl)(sc->epfd, EPOLL_CTL_DEL, sfd, &event, private); r_close(sc->epfd); free(sc); } __attribute__((constructor)) static void init(void) { /* init the libc_clone pointer */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" libc_clone = dlsym (RTLD_NEXT, "clone"); #pragma GCC diagnostic pop } vuos-0.9.2/umvu/src/vu_thread_sd.c000066400000000000000000000042261476575172100171370ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include /* safe default value */ static struct syscall_extra_t default_extra; static struct syscall_descriptor_t default_sd = {.extra = &default_extra}; /* per thread syscall descriptor */ __thread struct syscall_descriptor_t *thread_sd = &default_sd; /* set the new value of sd and return the previous, so that it can be used later to restore the old value */ struct syscall_descriptor_t *set_thread_sd(struct syscall_descriptor_t *sd) { struct syscall_descriptor_t *tmp = thread_sd; printkdebug(t, "set_thread_sd %p->%p\n", tmp, sd); thread_sd = sd; return tmp; } struct syscall_descriptor_t *get_thread_sd(void) { printkdebug(t, "get_thread_sd %p\n", thread_sd); return thread_sd; } static void *thread_sd_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_PTHREAD_CLONE: ret_value = thread_sd; printkdebug(t, "thread_sd_upcall CLONE %p", thread_sd); break; case INH_PTHREAD_START: thread_sd = ioarg; printkdebug(t, "thread_sd_upcall START %p\n", thread_sd); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_inheritance_upcall_register(thread_sd_upcall); debug_set_name(t, "THREADS"); } vuos-0.9.2/umvu/src/vu_tmpdir.c000066400000000000000000000030411476575172100164730ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #define TMP_PATTERN "/tmp/.vu_%010lu_XXXXXX" #define TMP_PATTERN_EXAMPLE "/tmp/.vu_0123456789_XXXXXX" static char dirpath[sizeof(TMP_PATTERN_EXAMPLE)+1]; char *vu_tmpdirpath(void) { return dirpath; } static void dirpath_init(void) { snprintf(dirpath, sizeof(TMP_PATTERN_EXAMPLE)+1, TMP_PATTERN, (unsigned long) getpid()); fatal(mkdtemp(dirpath)); r_chdir(dirpath); } static void dirpath_fini(void) { r_rmdir(dirpath); } __attribute__((constructor)) static void init (void) { vu_constructor_register(dirpath_init); vu_destructor_register(dirpath_fini); } vuos-0.9.2/umvu/src/vu_uidgid.c000066400000000000000000000112671476575172100164520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include /* In Linux all uid/gid are defined per thread level */ /* file system entry */ struct vu_uidgid_t { uid_t ruid, euid, suid, fsuid; gid_t rgid, egid, sgid, fsgid; int supgid_size; gid_t supgid[]; }; static __thread struct vu_uidgid_t *vu_uidgid = NULL; void vu_uidgid_getresfuid(uid_t *ruid, uid_t *euid, uid_t *suid, uid_t *fsuid) { if (ruid != NULL) *ruid = vu_uidgid->ruid; if (euid != NULL) *euid = vu_uidgid->euid; if (suid != NULL) *suid = vu_uidgid->suid; if (fsuid != NULL) *fsuid = vu_uidgid->fsuid; } void vu_uidgid_setresfuid(const uid_t ruid, const uid_t euid, const uid_t suid, const uid_t fsuid) { if (ruid != (uid_t) -1) vu_uidgid->ruid = ruid; if (euid != (uid_t) -1) vu_uidgid->euid = euid; if (suid != (uid_t) -1) vu_uidgid->suid = suid; if (fsuid != (uid_t) -1) vu_uidgid->fsuid = fsuid; } void vu_uidgid_getresfgid(gid_t *rgid, gid_t *egid, gid_t *sgid, gid_t *fsgid) { if (rgid != NULL) *rgid = vu_uidgid->rgid; if (egid != NULL) *egid = vu_uidgid->egid; if (sgid != NULL) *sgid = vu_uidgid->sgid; if (fsgid != NULL) *fsgid = vu_uidgid->fsgid; } void vu_uidgid_setresfgid(const gid_t rgid, const gid_t egid, const gid_t sgid, const gid_t fsgid) { if (rgid != (gid_t) -1) vu_uidgid->rgid = rgid; if (egid != (gid_t) -1) vu_uidgid->egid = egid; if (sgid != (gid_t) -1) vu_uidgid->sgid = sgid; if (fsgid != (gid_t) -1) vu_uidgid->fsgid = fsgid; } int vu_uidgid_getgroups(int size, gid_t list[]) { int supgid_size = vu_uidgid->supgid_size; if (size == 0) return supgid_size; else if (size < supgid_size) return errno = EINVAL, -1; else if (list == NULL) return errno = EFAULT, -1; else { memcpy(list, vu_uidgid->supgid, supgid_size * sizeof(gid_t)); return supgid_size; } } int vu_uidgid_setgroups(int size, gid_t list[]) { struct vu_uidgid_t *newuidgid; newuidgid = realloc(vu_uidgid, sizeof(struct vu_uidgid_t) + size * sizeof(gid_t)); if (newuidgid == NULL) return -1; else { vu_uidgid = newuidgid; vu_uidgid->supgid_size = size; memcpy(vu_uidgid->supgid, list, size * sizeof(gid_t)); return 0; } } static void vu_uidgid_create(void) { int supgid_size = getgroups(0, NULL); struct vu_uidgid_t *newuidgid; if (supgid_size < 0) supgid_size = 0; newuidgid = malloc(sizeof(struct vu_uidgid_t) + supgid_size * sizeof(gid_t)); fatal(newuidgid); getresuid(&newuidgid->ruid, &newuidgid->euid, &newuidgid->suid); getresgid(&newuidgid->rgid, &newuidgid->egid, &newuidgid->sgid); newuidgid->fsuid = setfsuid(-1); newuidgid->fsgid = setfsgid(-1); newuidgid->supgid_size = supgid_size; if (getgroups(supgid_size, newuidgid->supgid) < 0) warning(NULL); vu_uidgid = newuidgid; } static void *vu_uidgid_clone(int flags) { struct vu_uidgid_t *newuidgid; int supgid_size = vu_uidgid->supgid_size; newuidgid = malloc(sizeof(struct vu_uidgid_t) + supgid_size * sizeof(gid_t)); fatal(newuidgid); *newuidgid = *vu_uidgid; memcpy(newuidgid->supgid, vu_uidgid->supgid, supgid_size * sizeof(gid_t)); return newuidgid; } static void vu_uidgid_terminate(void) { xfree(vu_uidgid); } static void *vu_uidgid_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case INH_CLONE: ret_value = vu_uidgid_clone(*(int *)arg); break; case INH_PTHREAD_CLONE: ret_value = vu_uidgid_clone(0); break; case INH_START: case INH_PTHREAD_START: vu_uidgid = ioarg; break; case INH_TERMINATE: case INH_PTHREAD_TERMINATE: vu_uidgid_terminate(); break; default: break; } return ret_value; } __attribute__((constructor)) static void init(void) { vu_uidgid_create(); vu_inheritance_upcall_register(vu_uidgid_tracer_upcall); } vuos-0.9.2/umvu/src/vu_vnode.c000066400000000000000000000125571476575172100163230ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct vu_vnode_t { pthread_mutex_t mutex; struct vuht_entry_t *ht; dev_t dev; ino_t inode; off_t size; char *vpath; long usage_count; long flags; struct vu_vnode_t *next; }; /* XXX flags field has been added for mmap support when mmapped the file must be copied. read and mmap will then take place on the fake file in the tmpdir. a "dirty" bit can be used to support write ops on mmap. if the file is dirty it will need to be copied back. a rwlock seems to be needed to implement mmap. during the file copy (to the tmp dir) any I/O operation on the file need to be suspended */ #define VU_VNODE_HASH_SIZE 256 #define VU_VNODE_HASH_MASK (VU_VNODE_HASH_SIZE - 1) static pthread_mutex_t vnode_mutex = PTHREAD_MUTEX_INITIALIZER; static struct vu_vnode_t *vnode_hash[VU_VNODE_HASH_SIZE]; __attribute__((always_inline)) static inline int vnode_hashfun(struct vuht_entry_t *ht, dev_t dev, ino_t inode) { uintptr_t htint = (uintptr_t) ht; return (major(dev) + minor(dev) + inode + ((13 * htint) ^ (htint >> 13))) & VU_VNODE_HASH_MASK; } static struct vu_vnode_t **vnode_search(struct vuht_entry_t *ht, dev_t dev, ino_t inode) { struct vu_vnode_t **scan; for (scan = &vnode_hash[vnode_hashfun(ht, dev, inode)]; *scan != NULL; scan = &((*scan) -> next)) { struct vu_vnode_t *this = *scan; if (this->ht == ht && this->dev == dev && this->inode == inode) break; } return scan; } struct vu_vnode_t *vu_vnode_open(struct vuht_entry_t *ht, ino_t dev, ino_t inode, off_t size, int trunc) { struct vu_vnode_t **vnode_ptr; pthread_mutex_lock(&vnode_mutex); vnode_ptr = vnode_search(ht, dev, inode); if (*vnode_ptr == NULL) { struct vu_vnode_t *new_vnode = malloc(sizeof(struct vu_vnode_t)); fatal(new_vnode); pthread_mutex_init(&new_vnode->mutex, NULL); new_vnode->ht = ht; new_vnode->dev = dev; new_vnode->inode = inode; new_vnode->size = size; if (asprintf(&new_vnode->vpath, "%s/%p_%lx_%lx", vu_tmpdirpath(), (void *) ht, (unsigned long) dev, (unsigned long) inode) <= 0) { errno = ENOMEM; fatal(NULL); } new_vnode->usage_count = 1; new_vnode->flags = 0; new_vnode->next = NULL; *vnode_ptr = new_vnode; printkdebug(v, "vnode open %s count 1 (new)", new_vnode->vpath); } else { struct vu_vnode_t *this = *vnode_ptr; this->usage_count++; if (trunc) this->size = 0; printkdebug(v, "vnode open %s count %d", this->vpath, this->usage_count); }; pthread_mutex_unlock(&vnode_mutex); return *vnode_ptr; } void vu_vnode_close(struct vu_vnode_t *vnode) { pthread_mutex_lock(&vnode_mutex); printkdebug(v, "vnode close %s count %d", vnode->vpath, vnode->usage_count); if (--vnode->usage_count == 0) { struct vu_vnode_t **vnode_ptr = vnode_search(vnode->ht, vnode->dev, vnode->inode); struct vu_vnode_t *this = *vnode_ptr; fatal(this); *vnode_ptr = this->next; if (this->vpath) { /* XXX update file if mmapped and dirty */ pthread_mutex_destroy(&this->mutex); r_unlink(this->vpath); free(this->vpath); } free(this); } pthread_mutex_unlock(&vnode_mutex); } /* no lock needed, usage count guarantees that there are no risks */ char *vu_vnode_getvpath(struct vu_vnode_t *vnode) { return vnode->vpath; } int vu_vnode_copyinout (struct vu_vnode_t *vnode, char *path, copyfun cp) { int ret_value; pthread_mutex_lock(&vnode->mutex); ret_value = cp(vnode->ht, path, vnode->vpath); pthread_mutex_unlock(&vnode->mutex); return ret_value; } off_t vu_vnode_get_size_lock(struct vu_vnode_t *vnode) { pthread_mutex_lock(&vnode->mutex); return vnode->size; } void vu_vnode_set_size_unlock(struct vu_vnode_t *vnode, off_t size) { vnode->size = size; pthread_mutex_unlock(&vnode->mutex); } off_t vu_vnode_getset_size(struct vuht_entry_t *ht, ino_t dev, ino_t inode, off_t size) { off_t ret_value; struct vu_vnode_t **vnode_ptr; pthread_mutex_lock(&vnode_mutex); vnode_ptr = vnode_search(ht, dev, inode); struct vu_vnode_t *this = *vnode_ptr; if (this == NULL) ret_value = -1; else { pthread_mutex_lock(&this->mutex); ret_value = this->size; if (size >= 0) { /* r_truncate(this->vpath, size); */ /* truncate the local copy? */ this->size = size; } pthread_mutex_unlock(&this->mutex); } pthread_mutex_unlock(&vnode_mutex); return ret_value; } __attribute__((constructor)) static void init(void) { debug_set_name(v, "VNODE"); } vuos-0.9.2/umvu/src/vu_vwrap_vumgmt.c000066400000000000000000000167551476575172100177520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _VU_HYPERVISOR #include // to check consistecy with user libraries /* implementation of virtual syscalls. user processes can generate these requests using vulib */ void vw_insmod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct vuht_entry_t *sht; struct vu_service_t *service; char name[PATH_MAX]; char *modname; int permanent = (int)sd->syscall_args[1]; confirmfun_t cleanup; if (umvu_peek_str(sd->syscall_args[0], name, PATH_MAX) < 0) { sd->ret_value = -EINVAL; return; } service = module_load(name); if (service == NULL) { int save_errno = errno; printk(KERN_ERR "loading of module %s failed: %s\n", name, strerror(save_errno)); sd->ret_value = -save_errno; return; } modname = service->mod->name; if ((sht = vuht_pick(CHECKMODULE, modname, NULL, 0)) != NULL) { printk(KERN_ERR "module %s already loaded\n", modname); module_unload(service); sd->ret_value = -EEXIST; vuht_drop(sht); return; } /* insert the module as CHECKMODULE element in the hashtable. * - to maange the list of the currently available module * - "mount": CHECKFSTYPE chooses the module matching the module's name * as a prefix. */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" cleanup = module_getsym(service, "cleanup"); #pragma GCC diagnostic pop sht = vuht_add(CHECKMODULE, modname, strlen(modname), service, permanent ? VUFLAG_PERMANENT : 0, cleanup, NULL); service->service_ht = sht; vu_mod_setht(sht); module_run_init(service); sd->ret_value = 0; } void vw_rmmod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { char name[PATH_MAX]; struct vuht_entry_t *sht; struct vu_service_t *service; int ret_value; if (umvu_peek_str(sd->syscall_args[0], name, PATH_MAX) < 0) { sd->ret_value = -EINVAL; return; } sht = vuht_pick(CHECKMODULE, name, NULL, 0); if (sht == NULL) { printk(KERN_ERR "module %s is not loaded\n", name); sd->ret_value = -ENOENT; return; } service = vuht_get_service(sht); fatal(service); vu_mod_setht(sht); ret_value = module_run_fini(service); if (ret_value < 0) { sd->ret_value = -errno; printk(KERN_ERR "module %s: fini err %s\n", name, strerror(errno)); vuht_drop(sht); return; } if ((ret_value = vuht_del(sht, 0)) != 0) { sd->ret_value = ret_value; printk(KERN_ERR "module %s: %s\n", name, strerror(-ret_value)); vuht_drop(sht); return; } vuht_drop(sht); module_unload(service); update_vepoch(); sd->ret_value = 0; } static void list_item(struct vuht_entry_t *hte, void *arg) { FILE *f = arg; struct vu_service_t *s = vuht_get_service(hte); struct vu_module_t *m = s->mod; fprintf(f, "%s: %s\n", m->name, m->description); } void vw_lsmod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { syscall_arg_t buf_addr = sd->syscall_args[0]; unsigned int buf_size = (unsigned int)sd->syscall_args[1]; char *localbuf; size_t localbufsize; FILE *f = open_memstream(&localbuf, &localbufsize); forall_vuht_do(CHECKMODULE, list_item, f); fclose(f); localbufsize++; /* for the string terminator */ if (buf_addr != 0) { /*NULL*/ if (localbufsize < buf_size) buf_size = localbufsize; umvu_poke_data(buf_addr, localbuf, buf_size); } xfree(localbuf); sd->ret_value = localbufsize; } void vuctl_getinfo(struct syscall_descriptor_t *sd) { syscall_arg_t info_addr = sd->syscall_args[1]; if (info_addr != 0) { struct vu_info info; memset(&info, 0, sizeof(info)); r_uname(&info.uname); snprintf(info.vu_serverid, sizeof(info.vu_serverid), "%d", getpid()); get_vu_name(info.vu_name, sizeof(info.vu_name)); if (umvu_poke_data(info_addr, &info, sizeof(info)) < 0) sd->ret_value = -EINVAL; else sd->ret_value = 0; } else sd->ret_value = 0; } void vuctl_setname(struct syscall_descriptor_t *sd) { char vu_name[_UTSNAME_LENGTH+1]; syscall_arg_t name_addr = sd->syscall_args[1]; if (umvu_peek_str(name_addr, vu_name, _UTSNAME_LENGTH+1) < 0) { sd->ret_value = -EINVAL; return; } set_vu_name(vu_name); sd->ret_value = 0; } void vuctl_get_debugtags(struct syscall_descriptor_t *sd) { syscall_arg_t tags_addr = sd->syscall_args[1]; syscall_arg_t len = sd->syscall_args[2]; int local = sd->syscall_args[3]; char tags[DEBUG_NTAGS+1]; if (len > DEBUG_NTAGS+1) len = DEBUG_NTAGS+1; debug_get_tags(tags, len, local); if (umvu_poke_data(tags_addr, tags, strlen(tags) + 1) < 0) sd->ret_value = -EINVAL; else sd->ret_value = 0; } void vuctl_add_debugtags(struct syscall_descriptor_t *sd) { char tags[DEBUG_NTAGS+1]; syscall_arg_t tags_addr = sd->syscall_args[1]; int local = sd->syscall_args[2]; if (umvu_peek_str(tags_addr, tags, DEBUG_NTAGS+1) < 0) { sd->ret_value = -EINVAL; return; } debug_add_tags(tags, local); sd->ret_value = 0; } void vuctl_del_debugtags(struct syscall_descriptor_t *sd) { char tags[DEBUG_NTAGS+1]; syscall_arg_t tags_addr = sd->syscall_args[1]; int local = sd->syscall_args[2]; if (umvu_peek_str(tags_addr, tags, DEBUG_NTAGS+1) < 0) { sd->ret_value = -EINVAL; return; } debug_del_tags(tags, local); sd->ret_value = 0; } void vuctl_get_debugtagname(struct syscall_descriptor_t *sd) { syscall_arg_t tag = sd->syscall_args[1]; syscall_arg_t buf_addr = sd->syscall_args[2]; syscall_arg_t len = sd->syscall_args[3]; if (len > PATH_MAX) len = PATH_MAX; char buf[len]; debug_get_name(tag, buf, len); if (umvu_poke_data(buf_addr, buf, strlen(buf) + 1) < 0) sd->ret_value = -EINVAL; else sd->ret_value = 0; } void vuctl_setdebugcolors(struct syscall_descriptor_t *sd) { char colors[PATH_MAX]; syscall_arg_t colors_addr = sd->syscall_args[1]; if (umvu_peek_str(colors_addr, colors, PATH_MAX) < 0) { sd->ret_value = -EINVAL; return; } debug_set_color_string(colors); sd->ret_value = 0; } void vw_vuctl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { syscall_arg_t tag = sd->syscall_args[0]; switch (tag) { case VUCTL_GETINFO: vuctl_getinfo(sd); break; case VUCTL_SETNAME: vuctl_setname(sd); break; case VUCTL_GET_DEBUGTAGS: vuctl_get_debugtags(sd); break; case VUCTL_ADD_DEBUGTAGS: vuctl_add_debugtags(sd); break; case VUCTL_DEL_DEBUGTAGS: vuctl_del_debugtags(sd); break; case VUCTL_GET_DEBUGTAGNAME: vuctl_get_debugtagname(sd); break; case VUCTL_SET_DEBUGCOLOR: vuctl_setdebugcolors(sd); break; default: sd->ret_value = -EINVAL; } } vuos-0.9.2/umvu/src/vu_wrap_cap.c000066400000000000000000000061421476575172100167750ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_capget(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; uintptr_t hdrp = sd->syscall_args[0]; uintptr_t datap = sd->syscall_args[1]; cap_user_header_t hdr; cap_user_data_t data; sd->action = SKIPIT; if (hdrp == 0) { sd->ret_value = -EFAULT; return; } vu_alloc_peek_local_arg(hdrp, hdr, sizeof(*hdr), nested); if (hdr->version != _LINUX_CAPABILITY_VERSION_3) { sd->ret_value = -EFAULT; hdr->version = _LINUX_CAPABILITY_VERSION_3; vu_poke_arg(hdrp, hdr, sizeof(*hdr), nested); return; } if (datap == 0) { sd->ret_value = 0; return; } vu_alloc_local_arg(datap, data, sizeof(*data) * _LINUX_CAPABILITY_U32S_3, nested); if (ht) { sd->ret_value = service_syscall(ht, __VU_capget)(hdr, data); } else { sd->ret_value = capget(hdr, data); } if (sd->ret_value == 0) vu_poke_arg(datap, data, sizeof(*data) * _LINUX_CAPABILITY_U32S_3, nested); else sd->ret_value = -errno; } void wi_capset(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; uintptr_t hdrp = sd->syscall_args[0]; uintptr_t datap = sd->syscall_args[1]; cap_user_header_t hdr; cap_user_data_t data; sd->action = SKIPIT; if (hdrp == 0) { sd->ret_value = -EFAULT; return; } vu_alloc_peek_local_arg(hdrp, hdr, sizeof(*hdr), nested); if (hdr->version != _LINUX_CAPABILITY_VERSION_3) { sd->ret_value = -EFAULT; hdr->version = _LINUX_CAPABILITY_VERSION_3; vu_poke_arg(hdrp, hdr, sizeof(*hdr), nested); return; } if (datap == 0) { sd->ret_value = 0; return; } if (hdr->pid == 0) hdr->pid = umvu_gettid(); else if (hdr->pid != umvu_gettid()) { sd->ret_value = -EPERM; return; } vu_alloc_peek_local_arg(datap, data, sizeof(*data) * _LINUX_CAPABILITY_U32S_3, nested); sd->ret_value = service_syscall(ht, __VU_capset)(hdr, data); if (sd->ret_value != 0) sd->ret_value = -errno; } } vuos-0.9.2/umvu/src/vu_wrap_cwd.c000066400000000000000000000071761476575172100170170ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ /* management of current workinng directory */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* chdir, fchdir */ void wi_chdir(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (!nested) { int mode = sd->extra->statbuf.st_mode; if (S_ISDIR(mode)) { /* change the call to "chdir(fakedir)" */ sd->syscall_number = __NR_chdir; if (ht) /* If it's virtualized the chdir target is a dir that should always exist "/".*/ rewrite_syspath(sd, "/"); else /* Not virtualized, so chdir on the actual directory.*/ rewrite_syspath(sd, sd->extra->path); sd->action = DOIT_CB_AFTER; } else { if (mode == 0) sd->ret_value = -ENOENT; else if (sd->extra->path_errno != 0) sd->ret_value = -sd->extra->path_errno; else sd->ret_value = -ENOTDIR; sd->action = SKIPIT; } } } void wo_chdir(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int ret_value = sd->orig_ret_value; /* The path of the new cwd must be saved to support path canonicalization. * the current working dir changed only if the chdir syscall succeded */ if (ret_value >= 0) vu_fs_set_cwd(sd->extra->path); sd->ret_value = sd->orig_ret_value; } /* getcwd */ void wi_getcwd(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (!nested) { syscall_arg_t bufaddr = sd->syscall_args[0]; syscall_arg_t bufsize = sd->syscall_args[0]; char plaincwd[PATH_MAX]; char root[PATH_MAX]; char *cwd = plaincwd; size_t rootlen; size_t cwdlen; vu_fs_get_cwd(plaincwd, PATH_MAX); vu_fs_get_rootdir(root, PATH_MAX); if (root[1] == 0) root[0] = 0; rootlen = strlen(root); if (strncmp(plaincwd, root, rootlen) == 0) { if (cwd[rootlen] == '\0') strcpy(cwd, "/"); else cwd += rootlen; } cwdlen = strlen(cwd)+1; if (cwdlen < bufsize) bufsize = cwdlen; umvu_poke_data(bufaddr, cwd, bufsize); sd->ret_value = cwdlen; sd->action = SKIPIT; } } /* chroot */ void wi_chroot(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (!nested) { int mode = sd->extra->statbuf.st_mode; if (S_ISDIR(mode)) { vu_fs_set_rootdir(sd->extra->path); sd->action = SKIPIT; sd->ret_value = 0; } else { if (mode == 0) sd->ret_value = -ENOENT; else if (sd->extra->path_errno != 0) sd->ret_value = -sd->extra->path_errno; else sd->ret_value = -ENOTDIR; sd->action = SKIPIT; } } } vuos-0.9.2/umvu/src/vu_wrap_execve.c000066400000000000000000000264731476575172100175220ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* management of execve */ /* struct binfmt_req_t is defined in include vumodule.h */ static __thread struct vu_fnode_t *tmp_fnode; /* execve needs a real file. In order to support execve for virtual files, umvu makes a * temporary copy of the executable and then runs execve on the copy. * the following function rewrites the path of the execve system call */ static void rewrite_execve_filename(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, char *path, struct vu_stat *statbuf) { char *tmp_path; tmp_fnode = vu_fnode_create(ht, path, statbuf, 0, -1, VU_FNODE_CLOSED); tmp_path = vu_fnode_get_vpath(tmp_fnode); vu_fnode_copyin(tmp_fnode); rewrite_syspath(sd, tmp_path); } /* read the header of the executable to test if an interpreter is required. (e.g. using #! or binfmt_misc) */ static int read_exec_header(struct vuht_entry_t *ht, struct binfmt_req_t *req) { int ret_value; int fd; if (ht) { void *private = NULL; fd = service_syscall(ht, __VU_open)(vuht_path2mpath(ht, req->path), O_RDONLY, 0, &private); if (fd < 0) return -errno; if (service_getflags(ht) & VU_USE_PRW) ret_value = service_syscall(ht, __VU_pread64)(fd, req->filehead, BINFMTBUFLEN, 0, 0, private); else ret_value = service_syscall(ht, __VU_read)(fd, req->filehead, BINFMTBUFLEN, private); service_syscall(ht, __VU_close)(fd, private); } else { fd = r_open(req->path, O_RDONLY); if (fd < 0) return -errno; ret_value = r_read(fd, req->filehead, BINFMTBUFLEN); r_close(fd); } req->fileheadlen = ret_value; return ret_value; } /* the confirm function re-assigns the filehead field of the struct binfmt_req to "!%" + path of the interpreter */ static void check_binfmt_misc(struct binfmt_req_t *req) { struct vuht_entry_t *binfmt_ht; if ((binfmt_ht = vuht_pick(CHECKBINFMT, req, NULL, 0)) != NULL) vuht_drop(binfmt_ht); } static int need_interpreter(struct binfmt_req_t *req) { /* this heuristics should catch ELF, COFF and a.out */ if (req->fileheadlen <= 2 || req->filehead[0] < '\n' || req->filehead[0] == '\177') return 0; if (req->filehead[0] == '#' && req->filehead[1] == '!') return 1; req->fileheadlen = snprintf(req->filehead, BINFMTBUFLEN, "#!/bin/sh\n"); return 1; /* XXX unknown => script ?? */ } /* get args in the interpreter #! line */ int interpreter_fill_args(struct binfmt_req_t *req, char *argv[2]) { char *interpreter = req->filehead + 2; char *extra_arg; char *scan; char *term; if ((scan = memchr(req->filehead, '\n', req->fileheadlen)) != NULL) *scan = 0; if ((scan = memchr(req->filehead, '\0', req->fileheadlen)) == NULL || req->filehead[0] != '#' || req->filehead[1] != '!') { errno = EINVAL; return -1; } interpreter += strspn(interpreter, " \t"); scan = interpreter; scan += strcspn(scan, " \t\n"); term = scan; scan += strspn(scan, " \t"); extra_arg = scan; scan += strcspn(scan, "\n"); *scan = 0; *term = 0; argv[0] = interpreter; argv[1] = extra_arg; return *extra_arg == '\0' ? 1 : 2; } struct argv_item { uintptr_t arg; char *larg; struct argv_item *next; }; struct argv_list { int argc; struct argv_item *argv_head; }; static struct argv_list load_argv(struct syscall_descriptor_t *sd) { struct argv_list ret_value = { .argc = 0, .argv_head = NULL }; uintptr_t argv = sd->syscall_args[1]; struct argv_item **argv_item_scan = &ret_value.argv_head; while (1) { uintptr_t arg; struct argv_item *new; umvu_peek_data(argv, &arg, sizeof(uintptr_t)); if (arg == 0) break; ret_value.argc++; new = malloc(sizeof(struct argv_item)); fatal(new); new->arg = arg; new->larg = NULL; new->next = NULL; (*argv_item_scan) = new; argv_item_scan = &(new->next); argv += sizeof(uintptr_t); } return ret_value; } static void argv_behead(struct argv_list *list) { if (list->argv_head != NULL) { struct argv_item *old = list->argv_head; list->argv_head = list->argv_head->next; xfree(old); list->argc -= 1; } } static void argv_addhead(struct argv_list *list, uintptr_t arg, char *larg) { struct argv_item *new; new = malloc(sizeof(struct argv_item)); fatal(new); new->arg = arg; new->larg = larg; new->next = list->argv_head; list->argv_head = new; list->argc += 1; } static void copy_argv(uintptr_t *newargv, struct argv_list *argv) { struct argv_item *scan, *next; for (scan = argv->argv_head; scan != NULL; scan = next, newargv++) { *newargv = scan->arg; char *tmp; tmp = umvu_peekdup_path(scan->arg); xfree(tmp); next = scan->next; free(scan); } *newargv = 0; } static void push_argv(struct syscall_descriptor_t *sd, struct argv_list *argv) { struct argv_item *scan; for (scan = argv->argv_head; scan != NULL; scan = scan->next) { if (scan->arg == 0) scan->arg = vu_push(sd, scan->larg, strlen(scan->larg) + 1); } } static void rewrite_execve_argv(struct syscall_descriptor_t *sd, struct argv_list *argv_list) { uintptr_t newargv[argv_list->argc + 1]; push_argv(sd, argv_list); copy_argv(newargv, argv_list); sd->syscall_args[1] = vu_push(sd, newargv, sizeof(uintptr_t) * (argv_list->argc + 1)); } static int existence_check(struct syscall_descriptor_t *sd, struct vu_stat *buf) { if (buf->st_mode == 0) { sd->ret_value = -ENOENT; sd->action = SKIPIT; return -1; } else return 0; } static int xok_check(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, char *path) { int ret_value; ret_value = vu_access_emu(&sd->extra->statbuf, X_OK, AT_EACCESS); if (ret_value != 0) { sd->ret_value = -errno; sd->action = SKIPIT; return -1; } else return 0; } /* stuid/setgid must be forwarded to vu_mod_inheritance.c. uid/gid managing modules must be informed that the starting program is set[ug]id */ static void exec_setuid_setgid(struct vu_stat *statbuf) { if (statbuf->st_mode & S_ISUID) vu_exec_setuid(statbuf->st_uid); if (statbuf->st_mode & S_ISGID) vu_exec_setgid(statbuf->st_gid); } /* The interpreter itself may need an interpreter... this function processes this recursion up to EXECVE_MAX_DEPTH levels */ #define EXECVE_MAX_DEPTH 4 static void recursive_interpreter(struct binfmt_req_t *req, struct syscall_descriptor_t *sd, struct argv_list *argv_list, int depth) { struct binfmt_req_t new_req = { .path = NULL, .fileheadlen = 0, .flags = 0 }; char *extra_argv[2]; int extra_argc; struct vu_stat statbuf; struct vuht_entry_t *interpreter_ht; int ret_value; if (depth > EXECVE_MAX_DEPTH) { sd->ret_value = -ELOOP; sd->action = SKIPIT; return; } epoch_t e = set_vepoch(sd->extra->epoch); if ((extra_argc = interpreter_fill_args(req, extra_argv)) < 0) { sd->ret_value = -errno; sd->action = SKIPIT; return; } extra_argv[0] = get_path(AT_FDCWD, (syscall_arg_t) extra_argv[0], &statbuf, FOLLOWLINK, NULL, VU_NESTED); if (extra_argv[0] == NULL) { sd->ret_value = -errno; sd->action = SKIPIT; return; } if (existence_check(sd, &statbuf) < 0) { xfree(extra_argv[0]); return; } interpreter_ht = vuht_pick(CHECKPATH, extra_argv[0], &statbuf, SET_EPOCH); if (xok_check(interpreter_ht, sd, extra_argv[0]) < 0) { xfree(extra_argv[0]); if (interpreter_ht) vuht_drop(interpreter_ht); return; } if (!(req->flags & BINFMT_PRESERVE_ARGV0)) argv_behead(argv_list); if (depth == 1) argv_addhead(argv_list, sd->syscall_args[0], NULL); else argv_addhead(argv_list, 0, req->path); if (extra_argc > 1) argv_addhead(argv_list, 0, extra_argv[1]); argv_addhead(argv_list, 0, extra_argv[0]); new_req.path = extra_argv[0]; ret_value = read_exec_header(interpreter_ht, &new_req); if (ret_value < 0) { sd->ret_value = ret_value; sd->action = SKIPIT; xfree(extra_argv[0]); if (interpreter_ht) vuht_drop(interpreter_ht); return; } check_binfmt_misc(&new_req); if (vu_fs_is_chroot()) exec_chroot_rewrite_interpreter(interpreter_ht, &new_req); if (need_interpreter(&new_req)) { recursive_interpreter(&new_req, sd, argv_list, depth + 1); } else { exec_setuid_setgid(&statbuf); rewrite_execve_argv(sd, argv_list); if (interpreter_ht) { rewrite_execve_filename(interpreter_ht, sd, extra_argv[0], &statbuf); sd->action = DOIT_CB_AFTER; } else rewrite_syspath(sd, extra_argv[0]); } set_vepoch(e); xfree(extra_argv[0]); if (interpreter_ht) vuht_drop(interpreter_ht); } /* Wrapin for execve */ void wi_execve(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (!nested) { struct binfmt_req_t binfmt_req = { .path = sd->extra->path, .fileheadlen = 0, .flags = 0 }; int ret_value; //printk("execve %s %x ht=%p\n", sd->extra->path, sd->extra->statbuf.st_mode, ht); if (existence_check(sd, &sd->extra->statbuf) < 0) return; if (xok_check(ht, sd, sd->extra->path) < 0) return; ret_value = read_exec_header(ht, &binfmt_req); if (ret_value < 0) { sd->ret_value = ret_value; sd->action = SKIPIT; return; } check_binfmt_misc(&binfmt_req); if (vu_fs_is_chroot()) exec_chroot_rewrite_interpreter(ht, &binfmt_req); if (need_interpreter(&binfmt_req)) { struct argv_list argv_list = load_argv(sd); recursive_interpreter(&binfmt_req, sd, &argv_list, 1); } else { exec_setuid_setgid(&sd->extra->statbuf); if (ht) { rewrite_execve_filename(ht, sd, sd->extra->path, &sd->extra->statbuf); sd->action = DOIT_CB_AFTER; } } } } /* use inheritance to clean the temporary node */ static void clean_tmp_fnode(void) { if (tmp_fnode != NULL) { vu_fnode_close(tmp_fnode); tmp_fnode = NULL; } } /* if execve causes the "output" filter to run (after the syscall) it means that execve failed */ void wo_execve(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { clean_tmp_fnode(); sd->ret_value = sd->orig_ret_value; } static void *execve_tracer_upcall(inheritance_state_t state, void *ioarg, void *arg) { if (state == INH_EXEC) clean_tmp_fnode(); return NULL; } __attribute__((constructor)) static void init(void) { vu_inheritance_upcall_register(execve_tracer_upcall); } vuos-0.9.2/umvu/src/vu_wrap_file.c000066400000000000000000000602731476575172100171560ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* open, creat, openat */ /* all open files are registered in the file tables. * if the file is virtualized: * wi_open creates the f-node and then wo_open registers it in the fdtable. * the path of the real opensystem call is diverted to the tmpfile (see vnode.c) * otherwise: * if the real syscall request succeeds: wo_open creates an fnode and * registers it in the fdtable */ void wi_open(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int flags; int mode; void *private = NULL; /* local bufs */ /* fetch args */ switch (syscall_number) { case __NR_open: flags = sd->syscall_args[1]; mode = (flags & O_CREAT) || (flags & O_TMPFILE) ? sd->syscall_args[2] : 0; break; case __NR_creat: flags = O_CREAT|O_WRONLY|O_TRUNC; mode = sd->syscall_args[1]; break; case __NR_openat: flags = sd->syscall_args[2]; mode = (flags & O_CREAT) || (flags & O_TMPFILE) ? sd->syscall_args[3] : 0; break; default: default_nosys(sd); } mode = mode & ~vu_fs_get_umask(); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_open)(sd->extra->mpath, flags, mode, &private); if (ret_value < 0) { sd->ret_value = -errno; return; } else { struct vu_fnode_t *fnode; /* if the file has just been created by the service's open, the stat info must be updated */ if (sd->extra->statbuf.st_mode == 0) service_syscall(ht, __VU_lstat)(sd->extra->mpath, &sd->extra->statbuf, AT_SYMLINK_NOFOLLOW, ret_value, private); if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY)) sd->extra->statbuf.st_size = 0; fnode = vu_fnode_create(ht, sd->extra->path, &sd->extra->statbuf, flags, ret_value, private); vuht_pick_again(ht); if (nested) { /* do not use DOIT_CB_AFTER: open must be real, not further virtualized, wo_open won't be called. */ int fd; sd->ret_value = fd = r_open(vu_fnode_get_vpath(fnode), O_CREAT | O_RDWR, 0600); if (fd >= 0) vu_fd_set_fnode(fd, nested, fnode, flags); else vu_fnode_close(fnode); } else { sd->inout = fnode; sd->ret_value = ret_value; /* The user process opens a fake file in /tmp/.vu_... */ /* change the call to "openat(AT_FDCWD, vopen, O_CREAT | O_RDWR, 0600)" */ sd->syscall_number = __NR_openat; sd->syscall_args[0] = AT_FDCWD; rewrite_syspath(sd, vu_fnode_get_vpath(fnode)); sd->syscall_args[2] = O_CREAT | O_RDWR | (flags & O_CLOEXEC); sd->syscall_args[3] = 0600; sd->action = DOIT_CB_AFTER; } } } else sd->action = DOIT_CB_AFTER; } void wo_open(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int fd = sd->orig_ret_value; if (ht) { struct vu_fnode_t *fnode = sd->inout; int fdflags = sd->syscall_args[1] & O_CLOEXEC ? FD_CLOEXEC : 0; if (fd >= 0) { /* the user process has opened the fake file in /tmp/.vu.... * update the fdtable */ vu_fd_set_fnode(fd, VU_NOT_NESTED, fnode, fdflags); } else { /* the user process failed to open the fake file. close the virt-file */ vu_fnode_close(fnode); vuht_drop(ht); } } else { /* an entry in the file table is kept also when the user process opens real files. * the pathname of an open file is needed to support fchdir or the *at system calls * (like openat, fstatat...) */ if (fd >= 0) { struct vu_fnode_t *fnode; int fdflags; int nested = sd->extra->nested; switch (sd->syscall_number) { case __NR_open: fdflags = sd->syscall_args[1] & O_CLOEXEC ? FD_CLOEXEC : 0; break; case __NR_openat: fdflags = sd->syscall_args[1] & O_CLOEXEC ? FD_CLOEXEC : 0; break; default: fdflags = 0; } if (sd->extra->statbuf.st_mode == 0) /* new file just created */ r_lstat(sd->extra->path, &sd->extra->statbuf); fnode = vu_fnode_create(NULL, sd->extra->path, &sd->extra->statbuf, 0, -1, NULL); vu_fd_set_fnode(fd, nested, fnode, fdflags); } } sd->ret_value = sd->orig_ret_value; } /* close */ /* the kernel must effectilvely close the file anyway. it does not matter if the file is * real or virtual. When it is virtual it coses the fake file in /tmp/.vu.... */ /* vu_fd_close closes the fd in the fd table, then it cleans the element in the * file table if no more fds point to the file, then again the vnode is deallocated * and the fake file removed if no file table entries refer to the file */ void wi_close(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (nested) { /* do not use DOIT_CB_AFTER: close must be real, not further virtualized */ int fd = sd->syscall_args[0]; int ret_value = vu_fd_close(fd, VU_NESTED); sd->ret_value = ret_value < 0 ? -errno : 0; r_close(fd); sd->action = SKIPIT; } else { sd->action = DOIT_CB_AFTER; } } void wo_close(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int fd = sd->syscall_args[0]; int ret_value = sd->orig_ret_value; if (ret_value >= 0) vu_fd_close(fd, VU_NOT_NESTED); sd->ret_value = sd->orig_ret_value; } static int file_close_upcall(struct vuht_entry_t *ht, int sfd, void *private) { if (ht) { int ret_value; VU_HTWRAP(ht, ret_value = service_syscall(ht, __VU_close)(sfd, private)); vuht_drop(ht); return ret_value; } else return 0; } static ssize_t _file_read(struct vuht_entry_t *ht, int fd, int sfd, void *buf, size_t count, void *private, int nested, int slow) { if (slow == 0 && (service_getflags(ht) & VU_USE_PRW)) { ssize_t ret_value; off_t pos, size; vu_fd_get_possize_lock(fd, nested, &pos, &size); ret_value = service_syscall(ht, __VU_pread64)(sfd, buf, count, pos, 0, private); if (ret_value >= 0) pos += ret_value; vu_fd_set_possize_unlock(fd, nested, pos, size); return ret_value; } else return service_syscall(ht, __VU_read)(sfd, buf, count, private); } /* read, readv */ static void _file_wx_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, int slow) { int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; /* sfd is the "service file descriptor": the int value returned by the service * implementation of open */ int sfd = vu_fd_get_sfd(fd, &private, nested); if (sd->syscall_number == __NR_read) { uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; void *buf; ssize_t ret_value; vu_alloc_arg(addr, buf, bufsize, nested); ret_value = _file_read(ht, fd, sfd, buf, bufsize, private, nested, slow); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; if (ret_value > 0) vu_poke_arg(addr, buf, ret_value, nested); } vu_free_arg(buf, nested); } else { // readv uintptr_t iovaddr = sd->syscall_args[1]; int iovcnt = sd->syscall_args[2]; struct iovec *iov; void *buf; ssize_t ret_value; size_t bufsize; vu_alloc_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested); ret_value = _file_read(ht, sfd, fd, buf, bufsize, private, nested, slow); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; vu_poke_iov_arg(iovaddr, iov, iovcnt, buf, ret_value, nested); } vu_free_iov_arg(iov, buf, nested); } } void file_wi_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { _file_wx_read(ht, sd, 0); sd->action = SKIPIT; } } void slow_wi_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; if (!nested) { int fd = sd->syscall_args[0]; struct slowcall *sc = vu_slowcall_in(ht, fd, EPOLLIN, nested); if (sc != NULL) { sd->inout = sc; sd->action = BLOCKIT; return; } } _file_wx_read(ht, sd, 1); sd->action = SKIPIT; } } void slow_wd_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct slowcall *sc = sd->inout; sd->waiting_pid = vu_slowcall_during(sc); } void slow_wo_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; struct slowcall *sc = sd->inout; int fd = sd->syscall_args[0]; if (sc != NULL) { vu_slowcall_out(sc, ht, fd, EPOLLIN, nested); if (sd->waiting_pid != 0) { sd->ret_value = -EINTR; sd->action = SKIPIT; return; } } _file_wx_read(ht, sd, 1); } /* write, writev */ static ssize_t _file_write(struct vuht_entry_t *ht, int fd, int sfd, const void *buf, size_t count, void *private, int nested, int slow) { if (slow == 0 && (service_getflags(ht) & VU_USE_PRW)) { ssize_t ret_value; off_t pos, size; int flflags; flflags = vu_fd_get_flflags(fd, nested); vu_fd_get_possize_lock(fd, nested, &pos, &size); if (flflags > 0 && (flflags & O_APPEND)) pos = size; ret_value = service_syscall(ht, __VU_pwrite64)(sfd, buf, count, pos, 0, private); if (ret_value >= 0) { pos += ret_value; if (pos > size) size = pos; } vu_fd_set_possize_unlock(fd, nested, pos, size); return ret_value; } else return service_syscall(ht, __VU_write)(sfd, buf, count, private); } static void _file_wx_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, int slow) { if (ht) { int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); if (sd->syscall_number == __NR_write) { uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; void *buf; ssize_t ret_value; vu_alloc_peek_arg(addr, buf, bufsize, nested); ret_value = _file_write(ht, fd, sfd, buf, bufsize, private, nested, slow); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_arg(buf, nested); } else { // writev uintptr_t iovaddr = sd->syscall_args[1]; int iovcnt = sd->syscall_args[2]; struct iovec *iov; void *buf; ssize_t ret_value; size_t bufsize; vu_alloc_peek_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested); ret_value = _file_write(ht, fd, sfd, buf, bufsize, private, nested, slow); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_iov_arg(iov, buf, nested); } sd->action = SKIPIT; } } void file_wi_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { _file_wx_write(ht, sd, 0); sd->action = SKIPIT; } } void slow_wi_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; if (!nested) { int fd = sd->syscall_args[0]; struct slowcall *sc = vu_slowcall_in(ht, fd, EPOLLOUT, nested); if (sc != NULL) { sd->inout = sc; sd->action = BLOCKIT; return; } } _file_wx_write(ht, sd, 1); sd->action = SKIPIT; } } void slow_wd_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct slowcall *sc = sd->inout; sd->waiting_pid = vu_slowcall_during(sc); } void slow_wo_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; struct slowcall *sc = sd->inout; int fd = sd->syscall_args[0]; if (sc != NULL) { vu_slowcall_out(sc, ht, fd, EPOLLIN, nested); if (sd->waiting_pid != 0) { sd->ret_value = -EINTR; sd->action = SKIPIT; return; } } _file_wx_write(ht, sd, 1); } /* pread64, preadv, preadv2 */ void wi_pread(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int flags = 0; if (sd->syscall_number == __NR_pread64) { uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; off_t offset = sd->syscall_args[3]; void *buf; ssize_t ret_value; vu_alloc_arg(addr, buf, bufsize, nested); ret_value = service_syscall(ht, __VU_pread64)(sfd, buf, bufsize, offset, flags, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; if (ret_value > 0) vu_poke_arg(addr, buf, ret_value, nested); } vu_free_arg(buf, nested); } else { // preadv, preadv2 uintptr_t iovaddr = sd->syscall_args[1]; int iovcnt = sd->syscall_args[2]; off_t offset = sd->syscall_args[3]; if (sd->syscall_number == __NR_preadv2) flags = sd->syscall_args[4]; struct iovec *iov; void *buf; ssize_t ret_value; size_t bufsize; vu_alloc_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested); ret_value = service_syscall(ht, __VU_pread64)(sfd, buf, bufsize, offset, flags, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; vu_poke_iov_arg(iovaddr, iov, iovcnt, buf, ret_value, nested); } vu_free_iov_arg(iov, buf, nested); } sd->action = SKIPIT; } } /* pwrite64, pwritev, pwritev2 */ static ssize_t _file_pwrite(struct vuht_entry_t *ht, int fd, int sfd, const void *buf, size_t count, off_t pos, int flags, void *private, int nested) { if (service_getflags(ht) & VU_USE_PRW) { ssize_t ret_value; off_t rw_pos, size; /* rw_pos, pos used by read/write. unchanged here */ int flflags; flflags = vu_fd_get_flflags(fd, nested); vu_fd_get_possize_lock(fd, nested, &rw_pos, &size); if (flflags > 0 && (flflags & O_APPEND)) pos = size; ret_value = service_syscall(ht, __VU_pwrite64)(sfd, buf, count, pos, flags, private); if (ret_value >= 0) { pos += ret_value; if (pos > size) size = pos; } vu_fd_set_possize_unlock(fd, nested, rw_pos, size); return ret_value; } else return service_syscall(ht, __VU_pwrite64)(sfd, buf, count, pos, flags, private); } void wi_pwrite(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int flags = 0; if (sd->syscall_number == __NR_pwrite64) { uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; off_t offset = sd->syscall_args[3]; void *buf; ssize_t ret_value; vu_alloc_peek_arg(addr, buf, bufsize, nested); //ret_value = service_syscall(ht, __VU_pwrite64)(sfd, buf, bufsize, offset, flags, private); ret_value = _file_pwrite(ht, fd, sfd, buf, bufsize, offset, flags, private, nested); vu_free_arg(buf, nested); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } else { // pwritev, pwritev2 uintptr_t iovaddr = sd->syscall_args[1]; int iovcnt = sd->syscall_args[2]; off_t offset = sd->syscall_args[3]; if (sd->syscall_number == __NR_pwritev2) flags = sd->syscall_args[4]; struct iovec *iov; void *buf; ssize_t ret_value; size_t bufsize; vu_alloc_peek_iov_arg(iovaddr, iov, iovcnt, buf, bufsize, nested); //ret_value = service_syscall(ht, __VU_pwrite64)(sfd, buf, bufsize, offset, flags, private); ret_value = _file_pwrite(ht, fd, sfd, buf, bufsize, offset, flags, private, nested); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_iov_arg(iov, buf, nested); } sd->action = SKIPIT; } } /* getdents64, getdents */ void wi_getdents64(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addr = sd->syscall_args[1]; unsigned int bufsize = sd->syscall_args[2]; void *buf; int ret_value; vu_alloc_arg(addr, buf, bufsize, nested); ret_value = service_syscall(ht, __VU_getdents64)(sfd, buf, bufsize, private); if (ret_value < 0) sd->ret_value = -errno; else { /* The modules implement getdents64 only. umvu supports the (obsolete) * getdent using the mudule's getdents64 and converting the returned buffer */ if (sd->syscall_number == __NR_getdents) dirent64_to_dirent(buf, ret_value); vu_poke_arg(addr, buf, ret_value, nested); sd->ret_value = ret_value; } vu_free_arg(buf, nested); sd->action = SKIPIT; } } /* dup, dup2, dup3 */ /* dup is implemented as a builtin functionnality. Nested requests of dup * are simply executed and the fd table updated */ void wi_dup3(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (nested) { int fd = sd->syscall_args[0]; int newfd; int flags; switch (sd->syscall_number) { case __NR_dup: newfd = r_dup(fd); flags = 0; break; case __NR_dup2: newfd = sd->syscall_args[1]; newfd = r_dup2(fd, newfd); flags = 0; break; case __NR_dup3: newfd = sd->syscall_args[1]; flags = sd->syscall_args[2]; newfd = r_dup3(fd, newfd, flags); break; default: default_nosys(sd); } sd->action = SKIPIT; if (newfd < 0) sd->ret_value = -errno; else { if (newfd != fd) vu_fd_dup(newfd, VU_NESTED, fd, flags); sd->ret_value = newfd; } } else { sd->action = DOIT_CB_AFTER; } } void wo_dup3(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int newfd = sd->orig_ret_value; int fd = sd->syscall_args[0]; /* dup2 does nothing if fd == newfd */ if (newfd >= 0 && fd != newfd) { int flags = 0; if (sd->syscall_number == __NR_dup3) flags = sd->syscall_args[2]; vu_fd_dup(newfd, VU_NOT_NESTED, fd, flags); } sd->ret_value = newfd; } /* fcntl: * - F_[GS]ETFD, i.e. FD_CLOEXEC: it is a built-in feature, the fdtable support * closes the files when apporpriate on execs. * - F_[GS]ETFL, request forwarded to the module * - F_DUP*, it is similar to dup/dup2/dup3 here above */ void wi_fcntl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int fd = sd->syscall_args[0]; int cmd = sd->syscall_args[1]; int ret_value; if (ht) { void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); switch (cmd) { /* common mgmt virtual fd*/ case F_GETFD: ret_value = vu_fd_get_fdflags(fd, nested); sd->ret_value = (ret_value < 0) ? -EBADF : ret_value; sd->action = SKIPIT; return; case F_SETFD: { int flags = sd->syscall_args[2]; vu_fd_set_fdflags(fd, nested, flags); sd->ret_value = 0; /* DO IT */ } return; case F_GETFL: ret_value = service_syscall(ht, __VU_fcntl)(sfd, F_GETFL, 0, private); if (ret_value < 0) { sd->ret_value = -errno; if (errno == ENOSYS) sd->ret_value = vu_fd_get_flflags(fd, nested); } else sd->ret_value = ret_value; sd->action = SKIPIT; return; case F_SETFL: { int flags = sd->syscall_args[2]; ret_value = service_syscall(ht, __VU_fcntl)(sfd, F_SETFL, flags, private); if (ret_value < 0) { sd->ret_value = -errno; } else { sd->ret_value = ret_value; vu_fd_set_flflags(fd, nested, flags); } } return; } } else { switch (cmd) { /* common mgmt real fd*/ case F_GETFD: case F_GETFL: return; /* DOIT */ case F_SETFD: case F_SETFL: sd->action = DOIT_CB_AFTER; return; } } if (nested) { switch(cmd) { case F_DUPFD: case F_DUPFD_CLOEXEC: { int newfd; int arg = sd->syscall_args[2]; int flags = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0; newfd = fcntl(fd, cmd, arg); sd->action = SKIPIT; if (newfd < 0) sd->ret_value = -errno; else { if (newfd != fd) vu_fd_dup(newfd, VU_NESTED, fd, flags); sd->ret_value = newfd; } } return; } } else { switch(cmd) { case F_DUPFD: case F_DUPFD_CLOEXEC: sd->action = DOIT_CB_AFTER; return; } } } void wd_fcntl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { } void wo_fcntl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int fd = sd->syscall_args[0]; int cmd = sd->syscall_args[1]; int ret_value = sd->orig_ret_value; switch(cmd) { case F_DUPFD: case F_DUPFD_CLOEXEC: { int newfd = ret_value; int flags = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0; if (newfd >= 0 && fd != newfd) vu_fd_dup(newfd, VU_NOT_NESTED, fd, flags); } break; case F_SETFD: { int flags = sd->syscall_args[2]; if (ret_value >= 0) vu_fd_set_fdflags(fd, nested, flags); } break; case F_SETFL: { int flags = sd->syscall_args[2]; if (ret_value >= 0) vu_fd_set_flflags(fd, nested, flags); } break; } sd->ret_value = ret_value; } /* umask */ /* umask always succeeds. just copy the value */ void wi_umask(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (!nested) { int umask = sd->syscall_args[0]; vu_fs_set_umask(umask); } } /* lseek */ void wi_lseek(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; off_t ret_value; /* args */ int fd = sd->syscall_args[0]; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); off_t offset = sd->syscall_args[1]; int whence = sd->syscall_args[2]; /* call */ sd->action = SKIPIT; /* VU_USE_PRW */ if (service_getflags(ht) & VU_USE_PRW) { off_t pos, size; off_t modsize; vu_fd_get_possize_lock(fd, nested, &pos, &size); switch (whence) { case SEEK_SET: ret_value = offset; break; case SEEK_CUR: ret_value = pos + offset; break; case SEEK_END: modsize = service_syscall(ht, __VU_lseek)(sfd, 0, SEEK_END, private); if (modsize >= 0) ret_value = modsize + offset; else ret_value = size + offset; break; default: ret_value = -EINVAL; } if (ret_value >= 0) { off_t modpos = service_syscall(ht, __VU_lseek)(sfd, ret_value, SEEK_SET, private); if (modpos >= 0) ret_value = modpos; else if (errno != ENOSYS) ret_value = -errno; } if (ret_value >= 0) pos = ret_value; vu_fd_set_possize_unlock(fd, nested, pos, size); sd->ret_value = ret_value; } else { ret_value = service_syscall(ht, __VU_lseek)(sfd, offset, whence, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } } void wi_sendfile(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->action = SKIPIT; sd->ret_value = -ENOSYS; } void wi_copy_file_range(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->action = SKIPIT; sd->ret_value = -ENOSYS; } __attribute__((constructor)) static void init(void) { vu_fnode_set_close_upcall(S_IFREG, file_close_upcall); vu_fnode_set_close_upcall(S_IFDIR, file_close_upcall); vu_fnode_set_close_upcall(S_IFCHR, file_close_upcall); vu_fnode_set_close_upcall(S_IFBLK, file_close_upcall); vu_fnode_set_close_upcall(S_IFLNK, file_close_upcall); multiplex_read_wrappers(S_IFREG, file_wi_read, NULL, NULL); multiplex_read_wrappers(S_IFBLK, file_wi_read, NULL, NULL); multiplex_read_wrappers(S_IFCHR, slow_wi_read, slow_wd_read, slow_wo_read); multiplex_write_wrappers(S_IFREG, file_wi_write, NULL, NULL); multiplex_write_wrappers(S_IFBLK, file_wi_write, NULL, NULL); multiplex_write_wrappers(S_IFCHR, slow_wi_write, slow_wd_write, slow_wo_write); } vuos-0.9.2/umvu/src/vu_wrap_fs.c000066400000000000000000000513031476575172100166410ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017-2023 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* several similar system calls are processed by the same wrapper. * e.g. lstat stat fstat fstatat/newfstatat use the same wrapper. * the arg, fetching section of the wrapper takes the specific arguments of each system call */ /* lstat stat fstat fstatat/newfstatat */ void wi_lstat(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ syscall_arg_t bufaddr; int flags = AT_SYMLINK_NOFOLLOW; int sfd = -1; void *private = NULL; /* local bufs */ struct vu_stat *statbuf; /* fetch args */ switch (syscall_number) { case __NR_stat: case __NR_lstat: bufaddr = sd->syscall_args[1]; break; case __NR_fstat: bufaddr = sd->syscall_args[1]; sfd = sd->syscall_args[0]; sfd = (vu_fd_get_sfd(sfd, &private, nested)); break; case __NR_fstatat: case __NR_newfstatat: bufaddr = sd->syscall_args[2]; flags |= sd->syscall_args[3]; break; default: default_nosys(sd); } vu_alloc_local_arg(bufaddr, statbuf, sizeof(*statbuf), nested); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_lstat)(sd->extra->mpath, statbuf, flags, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } /* update st_size of currently open files */ if (S_ISREG(statbuf->st_mode) && (service_getflags(ht) & VU_USE_PRW)) { off_t opensize = vu_fnode_getset_size(ht, statbuf, -1); if (opensize >= 0) statbuf->st_size = opensize; } /* store results */ vu_poke_arg(bufaddr, statbuf, sizeof(*statbuf), nested); sd->ret_value = ret_value; } } /* statx */ void wi_statx(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; (void) syscall_number; /* args */ int flags = sd->syscall_args[2] | AT_SYMLINK_NOFOLLOW; int mask = sd->syscall_args[3]; syscall_arg_t bufaddr = sd->syscall_args[4]; /* local bufs */ struct statx *statxbuf; vu_alloc_local_arg(bufaddr, statxbuf, sizeof(*statxbuf), nested); /* call */ /* emulation mode. XXX - future module support? */ sd->action = SKIPIT; struct vu_stat statbuf; ret_value = service_syscall(ht, __VU_lstat)(sd->extra->mpath, &statbuf, flags, -1, NULL); if (ret_value < 0) { sd->ret_value = -errno; return; } (void) mask; // unused in emulation mode statxbuf->stx_mask = STATX_BASIC_STATS; statxbuf->stx_blksize = statbuf.st_blksize; statxbuf->stx_attributes = 0; statxbuf->stx_nlink = statbuf.st_nlink; statxbuf->stx_uid = statbuf.st_uid; statxbuf->stx_gid = statbuf.st_gid; statxbuf->stx_mode = statbuf.st_mode; statxbuf->stx_ino = statbuf.st_ino; statxbuf->stx_size = statbuf.st_size; statxbuf->stx_blocks = statbuf.st_blocks; statxbuf->stx_attributes_mask = 0; statxbuf->stx_atime.tv_sec = statbuf.st_atim.tv_sec; statxbuf->stx_atime.tv_nsec = statbuf.st_atim.tv_nsec; /* mtime -> btime */ statxbuf->stx_btime.tv_sec = statbuf.st_mtim.tv_sec; statxbuf->stx_btime.tv_nsec = statbuf.st_mtim.tv_nsec; statxbuf->stx_ctime.tv_sec = statbuf.st_ctim.tv_sec; statxbuf->stx_ctime.tv_nsec = statbuf.st_ctim.tv_nsec; statxbuf->stx_mtime.tv_sec = statbuf.st_mtim.tv_sec; statxbuf->stx_mtime.tv_nsec = statbuf.st_mtim.tv_nsec; statxbuf->stx_rdev_major = major(statbuf.st_rdev); statxbuf->stx_rdev_minor = minor(statbuf.st_rdev); statxbuf->stx_dev_major = major(statbuf.st_dev); statxbuf->stx_dev_minor = minor(statbuf.st_dev); /* store results */ vu_poke_arg(bufaddr, statxbuf, sizeof(*statxbuf), nested); sd->ret_value = ret_value; } } /* readlink, readlinkat */ void wi_readlink(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; ssize_t ret_value; /* args */ syscall_arg_t bufaddr; size_t bufsize; /* local bufs */ char *buf; size_t len; /* fetch args */ switch (syscall_number) { case __NR_readlink: bufaddr = sd->syscall_args[1]; bufsize = sd->syscall_args[2]; break; case __NR_readlinkat: bufaddr = sd->syscall_args[2]; bufsize = sd->syscall_args[3]; break; default: default_nosys(sd); } vu_alloc_local_arg(bufaddr, buf, PATH_MAX + 1, nested); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_readlink)(sd->extra->mpath, buf, PATH_MAX + 1); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ len = strlen(buf) + 1; if (len > bufsize) len = bufsize; vu_poke_arg(bufaddr, buf, len, nested); sd->ret_value = ret_value; } } /* access, faccessat, faccessat2 */ void wi_access(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int mode; int flags = 0; /* local bufs */ /* fetch args */ switch (syscall_number) { case __NR_access: mode = sd->syscall_args[1]; break; case __NR_faccessat: mode = sd->syscall_args[2]; break; case __NR_faccessat2: mode = sd->syscall_args[2]; flags = sd->syscall_args[3]; break; default: default_nosys(sd); } /* call */ sd->action = SKIPIT; /* Alternative #1: try the module's implementation of access and fall back on access emu if the module does not implement it. */ ret_value = service_syscall(ht, __VU_access)(sd->extra->mpath, mode, flags); if (ret_value < 0) { if (errno == ENOSYS) ret_value = vu_access_emu(&sd->extra->statbuf, mode, flags); else { sd->ret_value = -errno; return; } } /* Alternative #2: use the emulator anyway. */ #if 0 ret_value = vu_access_emu(&sd->extra->statbuf, mode, flags); #endif /* store results */ sd->ret_value = ret_value; } } /* unlink, unlinkat */ void wi_unlink(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int flags; /* local bufs */ /* fetch args */ switch (syscall_number) { case __NR_unlink: flags = 0; break; case __NR_unlinkat: flags = sd->syscall_args[2]; break; default: default_nosys(sd); } /* call */ sd->action = SKIPIT; /* If the AT_REMOVEDIR flag is specified, the unlink syscall acts as a rmdir */ if (flags & AT_REMOVEDIR) ret_value = service_syscall(ht, __VU_rmdir)(sd->extra->mpath); else ret_value = service_syscall(ht, __VU_unlink)(sd->extra->mpath); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* truncate, ftruncate */ void wi_truncate(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ off_t length = sd->syscall_args[1]; int sfd = -1; void *private = NULL; switch (syscall_number) { case __NR_truncate: sfd = -1; private = NULL; break; case __NR_ftruncate: sfd = sd->syscall_args[0]; sfd = (vu_fd_get_sfd(sfd, &private, nested)); break; } /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_truncate)(sd->extra->mpath, length, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } struct vu_stat *stat = &sd->extra->statbuf; /* update the size of open files */ if (S_ISREG(stat->st_mode) && (service_getflags(ht) & VU_USE_PRW)) vu_fnode_getset_size(ht, stat, length); sd->ret_value = ret_value; } } /* mkdir */ void wi_mkdir(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int mode; /* local bufs */ /* fetch args */ switch (syscall_number) { case __NR_mkdir: mode = sd->syscall_args[1]; break; case __NR_mkdirat: mode = sd->syscall_args[2]; break; default: default_nosys(sd); } mode = mode & ~vu_fs_get_umask(); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_mkdir)(sd->extra->mpath, mode); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* mknod */ void wi_mknod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int mode; dev_t dev; /* local bufs */ /* fetch args */ switch (syscall_number) { case __NR_mknod: mode = sd->syscall_args[1]; dev = sd->syscall_args[2]; break; case __NR_mknodat: mode = sd->syscall_args[2]; dev = sd->syscall_args[3]; break; default: default_nosys(sd); } mode = mode & ~vu_fs_get_umask(); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_mknod)(sd->extra->mpath, mode, dev); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* rmdir */ void wi_rmdir(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int ret_value; /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_rmdir)(sd->extra->mpath); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* lchown, fchown, chown, fchownat */ void wi_lchown(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ uid_t owner; gid_t group; /* __NR_fchownat flag AT_SYMLINK_NOFOLLOW has been already processes by the path resolution */ int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_lchown: case __NR_chown: owner = sd->syscall_args[1]; group = sd->syscall_args[2]; break; case __NR_fchown: owner = sd->syscall_args[1]; group = sd->syscall_args[2]; sfd = sd->syscall_args[0]; sfd = vu_fd_get_sfd(sfd, &private, nested); break; case __NR_fchownat: owner = sd->syscall_args[2]; group = sd->syscall_args[3]; /* flags = sd->syscall_args[4]; */ break; default: default_nosys(sd); } /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_lchown)(sd->extra->mpath, owner, group, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* chmod fchmod fchmodat */ void wi_chmod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ mode_t mode; /* __NR_fchmodat flag AT_SYMLINK_NOFOLLOW has been already processes by the path resolution */ int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_chmod: mode = sd->syscall_args[1]; break; case __NR_fchmod: mode = sd->syscall_args[1]; sfd = sd->syscall_args[0]; sfd = vu_fd_get_sfd(sfd, &private, nested); break; case __NR_fchmodat: #ifdef __NR_fchmodat2 case __NR_fchmodat2: #endif mode = sd->syscall_args[2]; /* flags = sd->syscall_args[3]; */ break; default: default_nosys(sd); } sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_chmod)(sd->extra->mpath, mode, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } static void utime2utimen(struct utimbuf *in_times, struct timespec *out_times) { out_times[0].tv_sec = in_times->actime; out_times[1].tv_sec = in_times->modtime; out_times[0].tv_nsec = out_times[1].tv_nsec = 0; } static void utimes2utimen(struct timeval *in_times, struct timespec *out_times) { out_times[0].tv_sec = in_times[0].tv_sec; out_times[1].tv_sec = in_times[1].tv_sec; out_times[0].tv_nsec = in_times[0].tv_usec * 1000; out_times[1].tv_nsec = in_times[1].tv_usec * 1000; } /* utimensat, utime, utimes, futimesat */ void wi_utimensat(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ int inarg = 1; struct timespec times[2]; int flags = 0; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_utime: case __NR_utimes: break; case __NR_futimesat: inarg = 2; break; case __NR_utimensat: { uintptr_t pathaddr = sd->syscall_args[1]; if (pathaddr == (uintptr_t) NULL) { vu_fd_get_sfd(sfd, &private, nested); sfd = vu_fd_get_sfd(sfd, &private, nested); } inarg = 2; flags = sd->syscall_args[3]; } break; } if (sd->syscall_args[inarg] == (uintptr_t) NULL) { clock_gettime(CLOCK_REALTIME, ×[0]); times[1] = times[0]; } else { if (nested) { switch (syscall_number) { case __NR_utime: { struct utimbuf *in_times = (struct utimbuf *) sd->syscall_args[inarg]; utime2utimen(in_times, times); } break; case __NR_utimes: case __NR_futimesat: { struct timeval *in_times = (struct timeval *) sd->syscall_args[inarg]; utimes2utimen(in_times, times); } break; case __NR_utimensat: { struct timespec *in_times = (struct timespec *) sd->syscall_args[inarg]; times[0] = in_times[0]; times[1] = in_times[1]; } break; } } else { uintptr_t addr = sd->syscall_args[inarg]; switch (syscall_number) { case __NR_utime: { struct utimbuf in_times; umvu_peek_data(addr, &in_times, sizeof(in_times)); utime2utimen(&in_times, times); } break; case __NR_utimes: case __NR_futimesat: { struct timeval in_times[2]; umvu_peek_data(addr, in_times, sizeof(in_times)); utimes2utimen(in_times, times); } break; case __NR_utimensat: umvu_peek_data(addr, times, sizeof(times)); break; } } } sd->action = SKIPIT; ret_value = service_syscall(ht,__VU_utimensat)(AT_FDCWD, sd->extra->mpath, times, flags, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* link, linkat */ void wi_link(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; char *oldpath; struct vuht_entry_t *htold; /* args */ int dirfd; uintptr_t oldaddr; epoch_t e; switch (syscall_number) { case __NR_link: dirfd = AT_FDCWD; oldaddr = sd->syscall_args[0]; break; case __NR_linkat: dirfd = sd->syscall_args[0]; oldaddr = sd->syscall_args[1]; break; default: default_nosys(sd); } sd->action = SKIPIT; if (sd->extra->statbuf.st_mode != 0) { sd->ret_value = -EEXIST; return; } e = set_vepoch(sd->extra->epoch); /* old/src path must be canonicalized */ oldpath = get_path(dirfd, oldaddr, NULL, 0, NULL, nested); if (oldpath == NULL) { sd->ret_value = -errno; set_vepoch(e); return; } htold = vuht_pick(CHECKPATH, oldpath, NULL, 0); vuht_drop(htold); /* oldpath and newpath are managed by different service modules. * link returns EXDEV as for paths on different mounted partitions */ set_vepoch(e); if (ht != htold) { xfree(oldpath); sd->ret_value = -EXDEV; return; } ret_value = service_syscall(ht, __VU_link)(vuht_path2mpath(ht, oldpath), sd->extra->mpath); xfree(oldpath); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* symlink, symlinkat */ void wi_symlink(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; int ret_value; char *target; if (nested) target = (char *) sd->syscall_args[0]; else target = umvu_peekdup_path(sd->syscall_args[0]); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_symlink)(target, sd->extra->mpath); if (!nested) xfree(target); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } /* rename, renameat, renameat2 */ void wi_rename(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; char *oldpath; struct vuht_entry_t *htold; /* args */ int dirfd; uintptr_t oldaddr; int flags = 0; epoch_t e; switch (syscall_number) { case __NR_rename: dirfd = AT_FDCWD; oldaddr = sd->syscall_args[0]; break; case __NR_renameat: dirfd = sd->syscall_args[0]; oldaddr = sd->syscall_args[1]; break; case __NR_renameat2: dirfd = sd->syscall_args[0]; oldaddr = sd->syscall_args[1]; flags = sd->syscall_args[4]; break; default: default_nosys(sd); } sd->action = SKIPIT; e = set_vepoch(sd->extra->epoch); /* old/src path must be canonicalized */ oldpath = get_path(dirfd, oldaddr, NULL, 0, NULL, nested); if (oldpath == NULL) { sd->ret_value = -errno; set_vepoch(e); return; } htold = vuht_pick(CHECKPATH, oldpath, NULL, 0); vuht_drop(htold); set_vepoch(e); /* oldpath and newpath are managed by different service modules. * rename returns EXDEV as for paths on different mounted partitions */ if (ht != htold) { xfree(oldpath); sd->ret_value = -EXDEV; return; } ret_value = service_syscall(ht, __VU_rename)(vuht_path2mpath(ht, oldpath), sd->extra->mpath, flags); if (ret_value < 0 && errno == ENOSYS) { /* workaround if rename is not available */ ret_value = service_syscall(ht, __VU_link)(vuht_path2mpath(ht, oldpath), sd->extra->mpath); if (ret_value == 0) ret_value = service_syscall(ht, __VU_unlink)(vuht_path2mpath(ht, oldpath), oldpath); } xfree(oldpath); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ sd->ret_value = ret_value; } } void wi_statfs(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int syscall_number = sd->syscall_number; int ret_value; /* args */ syscall_arg_t bufaddr = sd->syscall_args[1]; /* local bufs */ struct statfs *buf; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_statfs: break; case __NR_fstatfs: sfd = sd->syscall_args[0]; sfd = (vu_fd_get_sfd(sfd, &private, nested)); break; } vu_alloc_local_arg(bufaddr, buf, sizeof(*buf), nested); /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_statfs)(sd->extra->mpath, buf, sfd, private); if (ret_value < 0) { sd->ret_value = -errno; return; } /* store results */ vu_poke_arg(bufaddr, buf, sizeof(*buf), nested); sd->ret_value = ret_value; } } vuos-0.9.2/umvu/src/vu_wrap_ioctl.c000066400000000000000000000055311476575172100173450ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* TODO XXX ioctl can be a blocking syscall. ioctl should be changed to poll(NULL, 0, -1), the call of module's ioctl should be in the "during" phase, sending a PTRACE_INTERRUPT when done, and the results must be stored in the "out" phase */ void wi_ioctl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int ret_value; /* args */ int fd = sd->syscall_args[0]; unsigned long request = sd->syscall_args[1]; uintptr_t addr = sd->syscall_args[2]; unsigned long reqargs; int sfd; void *private = NULL; void *buf = NULL; int len; sd->action = SKIPIT; if (fd < 0) { sd->ret_value = -EBADF; return; } sfd = vu_fd_get_sfd(fd, &private, nested); /* module's ioctl returns the encoding of size and direction of the parameter i if fd == -1 */ /* modern ioctls have already size and direction encoded in their request argument, so if the modules' call fails, reqargs gets the value of request */ reqargs = service_syscall(ht, __VU_ioctl)(-1, request, NULL, addr, private); if (reqargs == (unsigned long) -1) reqargs = request; len = _IOC_SIZE(reqargs); if (len > 0) vu_alloc_arg(addr, buf, len, nested); if (reqargs & IOC_OUT) vu_peek_arg(addr, buf, len, nested); ret_value = service_syscall(ht, __VU_ioctl)(sfd, request, buf, addr, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; if (reqargs & IOC_IN) vu_poke_arg(addr, buf, len, nested); } if (buf) vu_free_arg(buf, nested); } } vuos-0.9.2/umvu/src/vu_wrap_mmap.c000066400000000000000000000064201476575172100171630ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_mmap(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int fd = sd->syscall_args[4]; int nested = sd->extra->nested; if (ht && fd >= 0) { // nothing to do if the file is real or is not a mmap on a file struct vu_fnode_t *fnode = vu_fd_get_fnode(fd, nested); vu_fnode_copyin(fnode); sd->inout = fnode; sd->action = DOIT_CB_AFTER; } } void wo_mmap(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct vu_fnode_t *fnode = sd->inout; if (fnode != NULL) { uintptr_t addr = sd->orig_ret_value; if (addr != (uintptr_t) -1) { //* if mmap has not failed */ size_t length = sd->syscall_args[1]; __attribute__((unused)) int prot = sd->syscall_args[2]; __attribute__((unused)) int flags = sd->syscall_args[3]; off_t offset = sd->syscall_args[5]; if (sd->syscall_number == __NR_mmap2) offset = offset * umvu_get_pagesize(); /* A specific data structure records the mapped areas.*/ vu_mmap_mmap(addr, length, fnode, offset); //printk("mmap %x %d %d %p\n", addr, length, offset, fnode); } else vu_fnode_close(fnode); } sd->ret_value = sd->orig_ret_value; } void wi_mm_cb_after(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->action = DOIT_CB_AFTER; } void wo_munmap(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; if (sd->ret_value != (uintptr_t) -1) { uintptr_t addr = sd->syscall_args[0]; size_t length = sd->syscall_args[1]; vu_mmap_munmap(addr, length); } } void wo_mremap(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { uintptr_t newaddr = sd->orig_ret_value; if (newaddr != (uintptr_t) -1) { uintptr_t oldaddr = sd->syscall_args[0]; size_t oldlength = sd->syscall_args[1]; size_t newlength = sd->syscall_args[2]; vu_mmap_mremap(oldaddr, oldlength, newaddr, newlength); } sd->ret_value = sd->orig_ret_value; } void wo_msync(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; } vuos-0.9.2/umvu/src/vu_wrap_mount.c000066400000000000000000000051151476575172100173730ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_mount(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht && !nested) { /* standard args */ int ret_value; /* args */ char *source = umvu_peekdup_path(sd->syscall_args[0]); char *target = sd->extra->path; char *filesystemtype = umvu_peekdup_path(sd->syscall_args[2]); unsigned long mountflags = sd->syscall_args[3]; char *data = NULL; if (sd->syscall_args[4] != 0) data = umvu_peekdup_path(sd->syscall_args[4]); /* fetch args */ /* call */ sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_mount)(source, target, filesystemtype, mountflags, data); xfree(source); xfree(filesystemtype); xfree(data); /* store results */ if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } void wi_umount2(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht && !nested) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ char *target = sd->extra->path; int flags; /* fetch args */ switch (syscall_number) { default: case __NR_umount: flags = 0; break; case __NR_umount2: flags = sd->syscall_args[1]; break; } sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_umount2)(target, flags); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } vuos-0.9.2/umvu/src/vu_wrap_poll.c000066400000000000000000000645241476575172100172100ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017, 2019 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef ERESTARTNOHAND #define ERESTARTNOHAND 514 #endif #ifndef ERESTART_RESTARTBLOCK #define ERESTART_RESTARTBLOCK 516 #endif /* epoll table: * it stores the list of virtual fds for epoll, poll and select */ struct epoll_tab_elem { int fd; int wepfd; struct vuht_entry_t *ht; int sfd; void *private; struct epoll_event event; struct epoll_tab_elem *next; }; struct epoll_tab { struct epoll_tab_elem *head; }; /* epoll info: this is for epoll only, the private field of the file table element of an epoll file points to this */ struct epoll_info { pthread_mutex_t mutex; int nested; struct epoll_tab tab; }; /* management of epoll table */ static struct epoll_tab epoll_tab_init(void) { struct epoll_tab ret_value = {NULL}; return ret_value; } static struct epoll_tab_elem *epoll_tab_search(struct epoll_tab tab, int fd) { struct epoll_tab_elem *scan; for (scan = tab.head; scan != NULL; scan = scan->next) { if (scan->fd == fd) return scan; } return NULL; } static inline struct epoll_tab_elem *epoll_tab_head(struct epoll_tab tab) { return tab.head; } static void epoll_tab_del(struct epoll_tab *tab, int fd) { struct epoll_tab_elem **scan; for (scan = &tab->head; *scan != NULL; scan = &((*scan)->next)) { struct epoll_tab_elem *this = *scan; if (this->fd == fd) { *scan = this->next; free(this); return; } } } static inline struct epoll_tab_elem *epoll_tab_alloc(void) { struct epoll_tab_elem *new = malloc(sizeof(struct epoll_tab_elem)); fatal(new); return new; } static void epoll_tab_add(struct epoll_tab *tab, struct epoll_tab_elem *new) { new->next = tab->head; tab->head = new; } static void epoll_tab_destroy(struct epoll_tab *tab) { while (tab->head != NULL) { struct epoll_tab_elem *tmp = tab->head; tab->head = tmp->next; xfree(tmp); } } /* epoll info management */ static struct epoll_info *epoll_info_create(int nested) { struct epoll_info *info = malloc(sizeof(struct epoll_info)); fatal(info); pthread_mutex_init(&info->mutex, NULL); info->nested = nested; info->tab = epoll_tab_init(); return info; } static void epoll_info_lock(struct epoll_info *info) { pthread_mutex_lock(&info->mutex); } static void epoll_info_unlock(struct epoll_info *info) { pthread_mutex_unlock(&info->mutex); } static void epoll_info_destroy(struct epoll_info *info) { if (info) { epoll_tab_destroy(&info->tab); pthread_mutex_destroy(&info->mutex); xfree(info); } } /* common wait code */ static void vu_poll_wait_thread(struct syscall_descriptor_t *sd, int epfd) { if ((sd->waiting_pid = r_fork()) == 0) { struct pollfd pfd = {epfd, POLLIN, 0}; //printk("epoll_thread... %d\n", epfd); //int ret_value = r_poll(&pfd, 1, -1); //printk("epoll_thread %d %d\n", ret_value, errno); r_exit(1); } } /* EPOLL: * epoll_create: set up a epollfd (epfd) in the hypervisor, create epoll_info and * register the user level file descriptor in the file table. * (for non nested only: epoll_create is a real epoll_create for nested virtualization) * * epoll_ctl/EPOLL_CTL_ADD: if ht != NULL (i.e. if it is a virtual file) create an epollfd (wepfd), * call the module's epoll_ctl/EPOLL_CTL_ADD, add wepfd as a file checked by epfd (EPOLLIN). * (this indirection is needed as if a user lever fd is duplicated the service file descriptor is the same * so it would not be possible to add both file descriptors in the same epoll set * * epoll_ctl/MOD: call module's epoll_ctl/MOD and update/delete the epoll table. * * epoll_wait: * (for non nested only: epoll_wait is a real epoll_wait for nested virtualization) * the hypervisor forks a wait watchdog process waiting on epfl, while the user process * is waiting on its epoll. * An event on a virtual fd causes the watchdog to terminate, otherwise the user process epoll_wait * exits if there is an event on a non-virtualized fd or a timeout. * the wo (wrap-out) epoll_wait code merges the events. */ void wi_epoll_create1(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->action = DOIT_CB_AFTER; } /* The process has just done the epoll_create. The file descriptor returend by epoll is used * by the process in the next epoll calls. The hypervisor uses it to refer to the fnode and * to its internal epoll fd. */ void wo_epoll_create1(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int fd = sd->orig_ret_value; if (fd >= 0) { /* standard args */ int syscall_number = sd->syscall_number; /* args */ int flags; int epfd; struct vu_fnode_t *fnode; struct epoll_info *info = epoll_info_create(nested); /* fetch args */ switch (syscall_number) { default: case __NR_epoll_create: flags = 0; break; case __NR_epoll_create1: flags = sd->syscall_args[0]; break; } epfd = r_epoll_create1(EPOLL_CLOEXEC); sd->extra->statbuf.st_mode = (sd->extra->statbuf.st_mode & ~S_IFMT) | S_IFEPOLL; sd->extra->statbuf.st_dev = 0; sd->extra->statbuf.st_ino = epfd; //printk("wo_epoll_create1 %d %d %p\n", fd, epfd, info); /* use the file table to map the user level fd to the (real) epollfd for modules */ fnode = vu_fnode_create(NULL, NULL, &sd->extra->statbuf, 0, epfd, info); vu_fd_set_fnode(fd, nested, fnode, (flags & EPOLL_CLOEXEC) ? FD_CLOEXEC : 0); } sd->ret_value = sd->orig_ret_value; } /* epoll_ctl_add/del/mod code is in three specific functions*/ static int epoll_ctl_add(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, int epfd,struct epoll_info *info, int sfd, void *private) { int nested = sd->extra->nested; int fd = sd->syscall_args[2]; uintptr_t eventaddr = sd->syscall_args[3]; struct epoll_tab_elem *epoll_elem = epoll_tab_search(info->tab, fd); if (epoll_elem != NULL) return -EEXIST; else { int wepfd = r_epoll_create1(EPOLL_CLOEXEC); //printk("wi_epoll_ctl ADD %d %p %d\n", fd, info, wepfd); if (wepfd < 0) return -errno; else { int retval; struct epoll_event *event; vu_alloc_peek_local_arg(eventaddr, event, sizeof(event), nested); struct epoll_event mod_event = {.events = event->events, .data.u64 = 0}; retval = service_syscall(ht, __VU_epoll_ctl)(wepfd, EPOLL_CTL_ADD, sfd, &mod_event, private); //printk("RETVAL %d %s\n", retval, strerror(errno)); if (retval < 0) { r_close(wepfd); return retval; } else { struct epoll_tab_elem *new = epoll_tab_alloc(); struct epoll_event welem = {.events = POLLIN, .data.ptr = new}; new->fd = fd; new->wepfd = wepfd; new->ht = ht; new->sfd = sfd; new->private = private; new->event = *event; epoll_tab_add(&info->tab, new); r_epoll_ctl(epfd, EPOLL_CTL_ADD, wepfd, &welem); } return retval; } } } static int epoll_ctl_mod(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, int epfd, struct epoll_info *info, int sfd, void *private) { int nested = sd->extra->nested; int fd = sd->syscall_args[2]; uintptr_t eventaddr = sd->syscall_args[3]; struct epoll_tab_elem *epoll_elem = epoll_tab_search(info->tab, fd); if (epoll_elem == NULL) return -ENOENT; else { int retval; struct epoll_event *event; //printk("wi_epoll_ctl MOD %d %p \n", fd, info); vu_alloc_peek_local_arg(eventaddr, event, sizeof(event), nested); struct epoll_event mod_event = {.events = event->events, .data.u64 = 0}; retval = service_syscall(ht, __VU_epoll_ctl)(epoll_elem->wepfd, EPOLL_CTL_MOD, sfd, &mod_event, private); if (retval >= 0) epoll_elem->event = *event; return retval; } } static int epoll_ctl_del(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd, int epfd, struct epoll_info *info, int sfd, void *private) { int fd = sd->syscall_args[2]; struct epoll_tab_elem *epoll_elem = epoll_tab_search(info->tab, fd); if (epoll_elem == NULL) return -ENOENT; else { int retval; //printk("wi_epoll_ctl DEL %d %p \n", fd, info); retval = service_syscall(ht, __VU_epoll_ctl)(epoll_elem->wepfd, EPOLL_CTL_DEL, sfd, NULL, private); if (retval >= 0) { r_close(epoll_elem->wepfd); epoll_tab_del(&info->tab, fd); } return retval; } } void wi_epoll_ctl(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int nested = sd->extra->nested; int proc_epfd = sd->syscall_args[0]; int op = sd->syscall_args[1]; int fd = sd->syscall_args[2]; int mode = vu_fd_get_mode(proc_epfd, nested); //printk("wi_epoll_ctl %d %p epfd %d, fd %d\n", sd->extra->nested, ht, proc_epfd, fd); sd->action = SKIPIT; if (nested) { uintptr_t eventaddr = sd->syscall_args[3]; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); struct epoll_event *event; vu_alloc_peek_local_arg(eventaddr, event, sizeof(event), nested); sd->ret_value = service_syscall(ht, __VU_epoll_ctl) (proc_epfd, op, sfd, event, private); //printk("NESTED EPOLL %d %d %d -> %d\n", proc_epfd, op, sfd, sd->ret_value); return; } if (mode == 0) sd->ret_value = EBADF; else if ((mode & S_IFMT) != S_IFEPOLL) sd->ret_value = -EINVAL; else { struct epoll_info *info; int epfd = vu_fd_get_sfd(proc_epfd, (void **) &info, nested); void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); epoll_info_lock(info); switch (op) { case EPOLL_CTL_ADD: sd->ret_value = epoll_ctl_add(ht, sd, epfd, info, sfd, private); break; case EPOLL_CTL_DEL: sd->ret_value = epoll_ctl_del(ht, sd, epfd, info, sfd, private); break; case EPOLL_CTL_MOD: sd->ret_value = epoll_ctl_mod(ht, sd, epfd, info, sfd, private); break; default: sd->ret_value = -EINVAL; } epoll_info_unlock(info); } } } static int epoll_close_upcall(struct vuht_entry_t *ht, int epfd, void *private) { struct epoll_info *info = private; epoll_info_lock(info); struct epoll_tab_elem *head; //printk("epoll_close_upcall %d %p\n", epfd, info); while ((head = epoll_tab_head(info->tab)) != NULL) { //printk("wi_epoll_ctl DEL %d %p \n", head->fd, info); VU_HTWRAP(head->ht, service_syscall(head->ht, __VU_epoll_ctl)(head->wepfd, EPOLL_CTL_DEL, head->sfd, NULL, head->private)); r_close(head->wepfd); epoll_tab_del(&info->tab, head->fd); } epoll_info_unlock(info); epoll_info_destroy(info); r_close(epfd); return 0; } void wi_epoll_wait(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int pepfd = sd->syscall_args[0]; struct epoll_info *info; __attribute__((unused)) int epfd = vu_fd_get_sfd(pepfd, (void **) &info, nested); //printk("wi_epoll_wait n%d %d %p %p\n", nested, epfd, info, epoll_tab_head(info->tab)); /* if there are already pending events do not stop and skip to the wo */ if (epoll_tab_head(info->tab) != NULL) sd->action = DOIT_CB_AFTER; } /* Blocking syscall: the process needs to wait for events (maybe on both real and virtual fds) * The hypervisor cannnot be blocked, so a new process waits for events on virtualized fds on * the epoll file descriptor (the sfd corresponding to the user's fd) */ void wd_epoll_wait(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int pepfd = sd->syscall_args[0]; struct epoll_info *info; int epfd = vu_fd_get_sfd(pepfd, (void **) &info, VU_NOT_NESTED); vu_poll_wait_thread(sd, epfd); } void wo_epoll_wait(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int pepfd = sd->syscall_args[0]; struct epoll_info *info; int epfd = vu_fd_get_sfd(pepfd, (void **) &info, nested); int orig_ret_value = sd->orig_ret_value; /* args */ uintptr_t eventaddr = sd->syscall_args[1]; struct epoll_event *events; int maxevents = sd->syscall_args[2]; //printk("wo_epoll_wait %d %d\n", maxevents, orig_ret_value); /* there are user events or the watchdog terminated and then the process got a PTRACE_INTERRUPT */ if(orig_ret_value >= 0 || orig_ret_value == -EINTR) { int ret_value = orig_ret_value; /* EINTR is not an error, it means that there are virtual events only */ if (ret_value < 0) ret_value = 0; /* there are "struct epoll_event" slots available */ if (maxevents > ret_value) { int available_events = maxevents - ret_value; int epoll_ret_value; int i; //printk("available_events %d\n", available_events); vu_alloc_peek_arg(eventaddr, events, maxevents * sizeof(struct epoll_event), nested); //printk("VU alloc okay\n"); epoll_ret_value = r_epoll_wait(epfd, events + ret_value, available_events, 0); //printk("epoll_ret_value %d\n", epoll_ret_value); epoll_info_lock(info); /* add the pending events of virtual file descriptors. Each ready-to-read wepfd has exactly one pending event. */ for (i = ret_value; i < ret_value + epoll_ret_value; i++) { struct epoll_tab_elem *elem = events[i].data.ptr; if (elem) { r_epoll_wait(elem->wepfd, &events[i], sizeof(struct epoll_event), nested); events[i].data = elem->event.data; } } epoll_info_unlock(info); if (epoll_ret_value > 0) { ret_value += epoll_ret_value; /* update the struct epoll_event (array) argument */ vu_poke_arg(eventaddr, events, ret_value * sizeof(struct epoll_event), nested); sd->ret_value = ret_value; } else sd->ret_value = orig_ret_value; vu_free_arg(events, nested); } } else sd->ret_value = orig_ret_value; } /* management of poll/ppoll */ struct poll_inout { int epfd; int nvirt; struct epoll_tab tab; int orig_fd[]; }; /* XXX TBD nested poll for hypervisor's threads */ void wi_poll(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; uintptr_t fdsaddr = sd->syscall_args[0]; int nfds = sd->syscall_args[1]; if (nfds < 0) { sd->ret_value = -EINVAL; sd->action = SKIPIT; return; } if (!nested) { struct pollfd *fds; struct epoll_tab tab = epoll_tab_init(); int i; vu_alloc_peek_arg(fdsaddr, fds, nfds * sizeof(struct pollfd), nested); for (i = 0; i < nfds; i++) { int fd = fds[i].fd; struct vuht_entry_t *fd_ht; /* if the fd is valid and virtual add it to the epoll table 'tab'*/ if (fd >= 0 && (fd_ht = vu_fd_get_ht(fd, nested)) != NULL) { struct epoll_tab_elem *elem; /* merge all the pollfd args referring to the same virtual file descriptor */ if ((elem = epoll_tab_search(tab, fd)) == NULL) { void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); elem = epoll_tab_alloc(); elem->fd = fd; elem->wepfd = -1; elem->ht = fd_ht; elem->sfd = sfd; elem->private = private; elem->event.events = 0; elem->event.data.ptr = elem; epoll_tab_add(&tab, elem); } elem->event.events |= fds[i].events; } } /* if there are virtual file descriptors */ if (epoll_tab_head(tab) != NULL) { struct poll_inout *pollio = malloc(sizeof(struct poll_inout) + nfds * sizeof(int)); struct epoll_tab_elem *scan; fatal(pollio); /* create an epoll fd */ pollio->epfd = r_epoll_create1(EPOLL_CLOEXEC); pollio->nvirt = 0; pollio->tab = tab; /* save the original file descriptors */ for (i = 0; i < nfds; i++) { int fd = fds[i].fd; if (vu_fd_get_ht(fd, nested) == NULL) pollio->orig_fd[i] = -1; else { pollio->orig_fd[i] = fds[i].fd; fds[i].fd = -1; } } /* add to the epoll set all the virtual file descriptors */ for (scan = epoll_tab_head(tab); scan != NULL; scan = scan->next) { pollio->nvirt++; //printk("EPOLL_CTL_ADD %d %d %p\n", pollio->epfd, scan->sfd, scan->private); VU_HTWRAP(scan->ht, service_syscall(scan->ht, __VU_epoll_ctl) (pollio->epfd, EPOLL_CTL_ADD, scan->sfd, &scan->event, scan->private)); // XXX error mgmt? } sd->inout = pollio; sd->action = DOIT_CB_AFTER; /* store the modified struct pollfd array */ vu_poke_arg(fdsaddr, fds, nfds * sizeof(struct pollfd), nested); } vu_free_arg(fds, nested); } } /* Blocking syscall: start a "waiting process" as explained for wd_epoll_wait here above */ void wd_poll(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct poll_inout *pollio = sd->inout; vu_poll_wait_thread(sd, pollio->epfd); } static inline int poll_add_events(int nfds, struct pollfd *fds, int fd, uint32_t events) { int count = 0; int i; for (i = 0; i < nfds; i++) { if (fds[i].fd == fd) { uint32_t revents = events & fds[i].events; if (fds[i].revents == 0 && revents != 0) count++; fds[i].revents |= revents; } } return count; } void wo_poll(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct poll_inout *pollio = sd->inout; uintptr_t fdsaddr = sd->syscall_args[0]; int nfds = sd->syscall_args[1]; struct pollfd *fds; int orig_ret_value = sd->orig_ret_value; struct epoll_tab_elem *scan; vu_alloc_peek_arg(fdsaddr, fds, nfds * sizeof(struct pollfd), VU_NOT_NESTED); int i; /* restore file descriptors */ for (i = 0; i < nfds; i++) { if (pollio->orig_fd[i] >= 0) { fds[i].fd = pollio->orig_fd[i]; // fds[i].fd was set to -1, virt fd fds[i].revents = 0; // it should already be 0, set here again for safety } } /* the user level poll terminated first: events on real fds only */ if (sd->waiting_pid != 0) sd->ret_value = orig_ret_value; else { /* EINTR/ERESTARTNOHAND are not error for us, just the watchdog terminated */ if (orig_ret_value == -EINTR || orig_ret_value == -ERESTARTNOHAND || orig_ret_value == -ERESTART_RESTARTBLOCK) orig_ret_value = 0; if (orig_ret_value < 0) sd->ret_value = orig_ret_value; else { /* get nvirtmax events and store revents in the struct pollfd elements */ struct epoll_event eventv[pollio->nvirt]; int nepoll; nepoll = r_epoll_wait(pollio->epfd, eventv, pollio->nvirt, 0); for (i = 0; i < nepoll; i++) { scan = eventv[i].data.ptr; orig_ret_value += poll_add_events(nfds, fds, scan->fd, eventv[i].events); } } sd->ret_value = orig_ret_value; } /* de-register the epoll requests for modules */ for (scan = epoll_tab_head(pollio->tab); scan != NULL; scan = scan->next) { VU_HTWRAP(scan->ht, service_syscall(scan->ht, __VU_epoll_ctl) (pollio->epfd, EPOLL_CTL_DEL, scan->sfd, NULL, scan->private)); // XXX error mgmt? } epoll_tab_destroy(&pollio->tab); r_close(pollio->epfd); vu_poke_arg(fdsaddr, fds, nfds * sizeof(struct pollfd), VU_NOT_NESTED); vu_free_arg(fds, VU_NOT_NESTED); xfree(pollio); } /* management of select pselect */ /* select pselect */ #define FD_SET_SIZE(nfds) ((__FD_ELT(nfds) + 1) * sizeof(__fd_mask)) #define EPOLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | EPOLLHUP | EPOLLERR) /* Ready for reading */ #define EPOLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | EPOLLERR) /* Ready for writing */ #define EPOLLEX_SET (EPOLLPRI) /* Exceptional condition */ #define EPOLL_SELECT_SET (EPOLLIN | EPOLLOUT | EPOLLPRI) struct select_inout { int epfd; int nvirt; struct epoll_tab tab; }; static inline void peek_fd_set(uintptr_t fdsetaddr, fd_set *fdset, int nfds, int nested) { if (fdsetaddr == 0) FD_ZERO(fdset); else vu_peek_arg(fdsetaddr, fdset, FD_SET_SIZE(nfds), nested); } static inline void poke_fd_set(uintptr_t fdsetaddr, fd_set *fdset, int nfds, int nested) { if (fdsetaddr != 0) vu_poke_arg(fdsetaddr, fdset, FD_SET_SIZE(nfds), nested); } static inline uint32_t fd_set2events(int fd, fd_set *r, fd_set *w, fd_set *x) { uint32_t events = 0; if (FD_ISSET(fd, r)) events |= EPOLLIN_SET; if (FD_ISSET(fd, w)) events |= EPOLLOUT_SET; if (FD_ISSET(fd, x)) events |= EPOLLEX_SET; return events; } static inline int events2fd_set(int fd, uint32_t events, uint32_t revents, fd_set *r, fd_set *w, fd_set *x) { int count = 0; if (events & EPOLLIN && revents & EPOLLIN_SET) FD_SET(fd, r), count++; if (events & EPOLLOUT && revents & EPOLLOUT_SET) FD_SET(fd, w), count++; if (events & EPOLLPRI && revents & EPOLLEX_SET) FD_SET(fd, x), count++; return count; } static inline void fd_set_fdclr(int fd, fd_set *r, fd_set *w, fd_set *x) { FD_CLR(fd, r); FD_CLR(fd, w); FD_CLR(fd, x); } /* XXX TBD nested select for hypervisor's threads */ void wi_select(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int nfds = sd->syscall_args[0]; uintptr_t readfdsaddr = sd->syscall_args[1]; uintptr_t writefdsaddr = sd->syscall_args[2]; uintptr_t exceptfdsaddr = sd->syscall_args[3]; if (nfds < 0) { sd->ret_value = -EINVAL; sd->action = SKIPIT; return; } if (!nested) { struct epoll_tab tab = epoll_tab_init(); int fd; fd_set readfds, writefds, exceptfds; peek_fd_set(readfdsaddr, &readfds, nfds, nested); peek_fd_set(writefdsaddr, &writefds, nfds, nested); peek_fd_set(exceptfdsaddr, &exceptfds, nfds, nested); /* search for events requested on virtual file descriptors, add virtual file descriptors (with attended events) to the epoll file table 'tab' */ for (fd = 0; fd < nfds; fd++) { uint32_t events = fd_set2events(fd, &readfds, &writefds, &exceptfds); if (events != 0) { struct vuht_entry_t *fd_ht; if ((fd_ht = vu_fd_get_ht(fd, nested)) != NULL) { void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); struct epoll_tab_elem *new = epoll_tab_alloc(); struct epoll_event event = {.events = events, .data.ptr = new}; fd_set_fdclr(fd, &readfds, &writefds, &exceptfds); new->fd = fd; new->wepfd = -1; new->ht = fd_ht; new->sfd = sfd; new->private = private; new->event = event; epoll_tab_add(&tab, new); } } } /* if select involves virtual file descriptors */ if (epoll_tab_head(tab) != NULL) { struct select_inout *selectio = malloc(sizeof(struct select_inout)); struct epoll_tab_elem *scan; fatal(selectio); selectio->epfd = r_epoll_create1(EPOLL_CLOEXEC); selectio->nvirt = 0; selectio->tab = tab; for (scan = epoll_tab_head(tab); scan != NULL; scan = scan->next) { selectio->nvirt++; //printk("EPOLL_CTL_ADD %d %d %p\n", selectio->epfd, scan->sfd, scan->private); VU_HTWRAP(scan->ht, service_syscall(scan->ht, __VU_epoll_ctl) (selectio->epfd, EPOLL_CTL_ADD, scan->sfd, &scan->event, scan->private)); // XXX error mgmt? } poke_fd_set(readfdsaddr, &readfds, nfds, nested); poke_fd_set(writefdsaddr, &writefds, nfds, nested); poke_fd_set(exceptfdsaddr, &exceptfds, nfds, nested); sd->inout = selectio; sd->action = DOIT_CB_AFTER; } } } /* Blocking syscall: start a "waiting process" as explained for wd_epoll_wait here above */ void wd_select(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct select_inout *selectio = sd->inout; vu_poll_wait_thread(sd, selectio->epfd); } void wo_select(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = VU_NOT_NESTED; int orig_ret_value = sd->orig_ret_value; struct select_inout *selectio = sd->inout; struct epoll_tab_elem *scan; //printk("wo_select\n"); if (sd->waiting_pid != 0) // userland select terminated first, no virtual events sd->ret_value = orig_ret_value; else { int nfds = sd->syscall_args[0]; uintptr_t readfdsaddr = sd->syscall_args[1]; uintptr_t writefdsaddr = sd->syscall_args[2]; uintptr_t exceptfdsaddr = sd->syscall_args[3]; fd_set readfds, writefds, exceptfds; if (orig_ret_value == -EINTR || orig_ret_value == -ERESTARTNOHAND || orig_ret_value == -ERESTART_RESTARTBLOCK) { /* interrupted syscall: virtual events only. */ peek_fd_set(0, &readfds, nfds, nested); peek_fd_set(0, &writefds, nfds, nested); peek_fd_set(0, &exceptfds, nfds, nested); orig_ret_value = 0; } else { peek_fd_set(readfdsaddr, &readfds, nfds, nested); peek_fd_set(writefdsaddr, &writefds, nfds, nested); peek_fd_set(exceptfdsaddr, &exceptfds, nfds, nested); } if (orig_ret_value < 0) { /* restore the original fd_sets, as the manual says: On error, -1 is returned, and errno is set to indicate the error; the file descriptor sets are unmodified */ for (scan = epoll_tab_head(selectio->tab); scan != NULL; scan = scan->next) events2fd_set(scan->fd, scan->event.events, EPOLL_SELECT_SET, &readfds, &writefds, &exceptfds); sd->ret_value = orig_ret_value; } else { struct epoll_event events[selectio->nvirt]; int epollnfds, i; int ret_value = 0; epollnfds = r_epoll_wait(selectio->epfd, events, selectio->nvirt, 0); //printk("wo_select here n%d %d\n", epollnfds); for (i = 0; i < epollnfds; i++) { struct epoll_tab_elem *elem = events[i].data.ptr; //printk("wo_select elem %p %d\n", elem, elem->fd); if (elem) ret_value += events2fd_set(elem->fd, elem->event.events, events[i].events, &readfds, &writefds, &exceptfds); sd->ret_value = orig_ret_value + ret_value; //printk("wo_select final %d %d+%d\n", sd->ret_value, orig_ret_value, ret_value); } } poke_fd_set(readfdsaddr, &readfds, nfds, nested); poke_fd_set(writefdsaddr, &writefds, nfds, nested); poke_fd_set(exceptfdsaddr, &exceptfds, nfds, nested); } /* de-register the epoll requests for modules */ for (scan = epoll_tab_head(selectio->tab); scan != NULL; scan = scan->next) VU_HTWRAP(scan->ht, service_syscall(scan->ht, __VU_epoll_ctl) (selectio->epfd, EPOLL_CTL_DEL, scan->sfd, NULL, scan->private)); // XXX error mgmt? epoll_tab_destroy(&selectio->tab); r_close(selectio->epfd); xfree(selectio); } __attribute__((constructor)) static void init (void) { vu_fnode_set_close_upcall(S_IFEPOLL, epoll_close_upcall); } vuos-0.9.2/umvu/src/vu_wrap_rw_multiplex.c000066400000000000000000000071641476575172100207720ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include /* Read and write have specific behaviors depending upon the file type, e.g. ordinary files, * devices and network sockets. This module dispatches the read/write requests to * implementations specific to each type of file descriptor */ /* Umvu supports 16 file types as defined in umvu/include/xstat.h */ wrapf_t wi_NULL, wd_NULL, wo_NULL; static wrapf_t wi_einval; static wrapf_t *x_wi_read[S_TYPES] = {S_TYPES_INIT(wi_einval)}; static wrapf_t *x_wd_read[S_TYPES] = {S_TYPES_INIT(wd_NULL)}; static wrapf_t *x_wo_read[S_TYPES] = {S_TYPES_INIT(wo_NULL)}; static wrapf_t *x_wi_write[S_TYPES] = {S_TYPES_INIT(wi_einval)}; static wrapf_t *x_wd_write[S_TYPES] = {S_TYPES_INIT(wd_NULL)}; static wrapf_t *x_wo_write[S_TYPES] = {S_TYPES_INIT(wo_NULL)}; static void wi_einval(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht != NULL) { sd->ret_value = -EINVAL; sd->action = SKIPIT; } } void multiplex_read_wrappers(mode_t mode, wrapf_t wrapin, wrapf_t wrapduring, wrapf_t wrapout) { int modeindex = S_MODE2TYPE(mode); if (wrapin == NULL) x_wi_read[modeindex] = wi_einval; else x_wi_read[modeindex] = wrapin; if (wrapduring == NULL) x_wd_read[modeindex] = wd_NULL; else x_wd_read[modeindex] = wrapduring; if (wrapout == NULL) x_wo_read[modeindex] = wo_NULL; else x_wo_read[modeindex] = wrapout; } void multiplex_write_wrappers(mode_t mode, wrapf_t wrapin, wrapf_t wrapduring, wrapf_t wrapout) { int modeindex = S_MODE2TYPE(mode); if (wrapin == NULL) x_wi_write[modeindex] = wi_einval; else x_wi_write[modeindex] = wrapin; if (wrapduring == NULL) x_wd_write[modeindex] = wd_NULL; else x_wd_write[modeindex] = wrapduring; if (wrapout == NULL) x_wo_write[modeindex] = wo_NULL; else x_wo_write[modeindex] = wrapout; } void wi_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wi_read[S_MODE2TYPE(mode)](ht, sd); } void wi_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wi_write[S_MODE2TYPE(mode)](ht, sd); } void wd_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wd_read[S_MODE2TYPE(mode)](ht, sd); } void wd_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wd_write[S_MODE2TYPE(mode)](ht, sd); } void wo_read(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wo_read[S_MODE2TYPE(mode)](ht, sd); } void wo_write(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { mode_t mode = sd->extra->statbuf.st_mode; x_wo_write[S_MODE2TYPE(mode)](ht, sd); } vuos-0.9.2/umvu/src/vu_wrap_socket.c000066400000000000000000000605471476575172100175330ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include /* See NOTES */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_SOCKADDR_LEN sizeof(struct sockaddr_storage) void wi_socket(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int domain; int type; int protocol; int flags = 0; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_socket: domain = sd->syscall_args[0]; type = sd->syscall_args[1]; protocol = sd->syscall_args[2]; break; case __VVU_msocket: domain = sd->syscall_args[1]; type = sd->syscall_args[2]; protocol = sd->syscall_args[3]; break; default: default_nosys(sd); } if (type & SOCK_CLOEXEC) flags |= O_CLOEXEC; if (type & SOCK_CLOEXEC) flags |= O_NONBLOCK; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_socket)(domain, type, protocol, &private); if (ret_value < 0) { sd->ret_value = -errno; return; } else { struct vu_fnode_t *fnode; /* fake dev = 0, inode = sfd */ sd->extra->statbuf.st_mode = (sd->extra->statbuf.st_mode & ~S_IFMT) | S_IFSOCK; sd->extra->statbuf.st_dev = 0; sd->extra->statbuf.st_ino = ret_value; fnode = vu_fnode_create(ht, sd->extra->path, &sd->extra->statbuf, 0, ret_value, private); vuht_pick_again(ht); if (nested) { /* do not use DOIT_CB_AFTER: open must be real, not further virtualized */ int fd; sd->ret_value = fd = r_open(vu_fnode_get_vpath(fnode), O_CREAT | O_RDWR, 0600); if (fd >= 0) vu_fd_set_fnode(fd, nested, fnode, flags); else vu_fnode_close(fnode); } else { sd->inout = fnode; sd->ret_value = ret_value; /* change the call to "openat(AT_FDCWD, vopen, O_CREAT | O_RDWR, 0600)" */ sd->syscall_number = __NR_openat; sd->syscall_args[0] = AT_FDCWD; rewrite_syspath(sd, vu_fnode_get_vpath(fnode)); sd->syscall_args[2] = O_CREAT | O_RDWR | (flags & O_CLOEXEC); sd->syscall_args[3] = 0600; sd->action = DOIT_CB_AFTER; } } } } void wo_socket(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int fd = sd->orig_ret_value; if (ht) { struct vu_fnode_t *fnode = sd->inout; int fdflags = sd->syscall_args[1] & O_CLOEXEC ? FD_CLOEXEC : 0; if (fd >= 0) { vu_fd_set_fnode(fd, VU_NOT_NESTED, fnode, fdflags); } else { vu_fnode_close(fnode); vuht_drop(ht); } } sd->ret_value = sd->orig_ret_value; } void vw_msocket(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) wi_socket(ht, sd); else if (sd->extra->statbuf.st_mode == 0) sd->ret_value = -ENOENT; else sd->ret_value = -ENOTSUP; } static int socket_close_upcall(struct vuht_entry_t *ht, int sfd, void *private) { if (ht) { int ret_value; VU_HTWRAP(ht, ret_value = service_syscall(ht, __VU_close)(sfd, private)); vuht_drop(ht); return ret_value; } else return 0; } void wi_bind(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addraddr = sd->syscall_args[1]; void *addr; socklen_t addrlen = sd->syscall_args[2]; if (addrlen > MAX_SOCKADDR_LEN) addrlen = MAX_SOCKADDR_LEN; vu_alloc_peek_local_arg(addraddr, addr, addrlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_bind)(sfd, addr, addrlen, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } void wi_connect(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addraddr = sd->syscall_args[1]; void *addr; socklen_t addrlen = sd->syscall_args[2]; if (addrlen > MAX_SOCKADDR_LEN) addrlen = MAX_SOCKADDR_LEN; vu_alloc_peek_local_arg(addraddr, addr, addrlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_connect)(sfd, addr, addrlen, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } void wi_listen(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int backlog = sd->syscall_args[1]; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_listen)(sfd, backlog, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } /* accept accept4 */ void wi_accept4(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { /* standard args */ int syscall_number = sd->syscall_number; int ret_value; /* args */ int fd = sd->syscall_args[0]; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addraddr = sd->syscall_args[1]; void *addr; uintptr_t paddrlen = sd->syscall_args[2]; socklen_t *addrlen; int flags = 0; int fflags = 0; /* accept can be a blocking syscall. The hypervisor checks if there are pending * connections, i.e. if accept would block or not. */ if (!nested) { int fd = sd->syscall_args[0]; struct slowcall *sc = vu_slowcall_in(ht, fd, EPOLLIN, nested); if (sc != NULL) { sd->inout = sc; if (vu_slowcall_test(sc) <= 0) { /* BLOCKIT implies UMVU_CB_AFTER, the "during" wrapper waits for a connection request */ sd->action = BLOCKIT; return; } else vu_slowcall_out(sc, ht, fd, EPOLLIN, nested); } } vu_alloc_peek_local_arg(paddrlen, addrlen, sizeof(socklen_t), nested); if (*addrlen > MAX_SOCKADDR_LEN) *addrlen = MAX_SOCKADDR_LEN; vu_alloc_local_arg(addraddr, addr, *addrlen, nested); if (syscall_number == __NR_accept4) flags = sd->syscall_args[3]; if (flags & SOCK_CLOEXEC) fflags |= O_CLOEXEC; if (flags & SOCK_CLOEXEC) fflags |= O_NONBLOCK; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_accept4)(sfd, addr, addrlen, flags, private, &private); if (ret_value < 0) sd->ret_value = -errno; else { struct vu_fnode_t *fnode; /* fake dev = 0, inode = sfd */ sd->extra->statbuf.st_mode = (sd->extra->statbuf.st_mode & ~S_IFMT) | S_IFSOCK; sd->extra->statbuf.st_dev = 0; sd->extra->statbuf.st_ino = ret_value; /* the service module has created a new socket, create the fnode element */ fnode = vu_fnode_create(ht, sd->extra->path, &sd->extra->statbuf, fflags, ret_value, private); vuht_pick_again(ht); if (nested) { /* do not use DOIT_CB_AFTER: open must be real, not further virtualized */ int fd; sd->ret_value = fd = r_open(vu_fnode_get_vpath(fnode), O_CREAT | O_RDWR, 0600); if (fd >= 0) vu_fd_set_fnode(fd, nested, fnode, fflags); else vu_fnode_close(fnode); } else { /* the user processes opens a fake file in /tmp/.vu.... instead */ sd->inout = fnode; sd->ret_value = ret_value; /* change the call to "openat(AT_FDCWD, vopen, O_CREAT | O_RDWR, 0600)" */ sd->syscall_number = __NR_openat; sd->syscall_args[0] = AT_FDCWD; rewrite_syspath(sd, vu_fnode_get_vpath(fnode)); sd->syscall_args[2] = O_CREAT | O_RDWR | (fflags & O_CLOEXEC); sd->syscall_args[3] = 0600; sd->action = DOIT_CB_AFTER; } vu_poke_arg(addraddr, addr, ret_value, nested); vu_poke_arg(paddrlen, addrlen, sizeof(socklen_t), nested); sd->ret_value = ret_value; } } } void wd_accept4(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (sd->action == BLOCKIT) { struct slowcall *sc = sd->inout; sd->waiting_pid = vu_slowcall_during(sc); } } void wo_accept4(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (sd->action == BLOCKIT) { int nested = sd->extra->nested; struct slowcall *sc = sd->inout; /* standard args */ int fd = sd->syscall_args[0]; if (sc != NULL) { vu_slowcall_out(sc, ht, fd, EPOLLIN, nested); if (sd->waiting_pid != 0) { sd->ret_value = -EINTR; sd->action = SKIPIT; return; } } /* now te fs is ready for reeading (accepting), restart the syscall */ sd->action = DO_IT_AGAIN; } else { int fd = sd->orig_ret_value; if (ht) { struct vu_fnode_t *fnode = sd->inout; int fdflags = sd->syscall_args[1] & O_CLOEXEC ? FD_CLOEXEC : 0; if (fd >= 0) { vu_fd_set_fnode(fd, VU_NOT_NESTED, fnode, fdflags); } else { vu_fnode_close(fnode); vuht_drop(ht); } } sd->ret_value = sd->orig_ret_value; } } void wi_getsockname(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addraddr = sd->syscall_args[1]; void *addr; uintptr_t paddrlen = sd->syscall_args[2]; socklen_t *addrlen; vu_alloc_peek_local_arg(paddrlen, addrlen, sizeof(socklen_t), nested); if (*addrlen > MAX_SOCKADDR_LEN) *addrlen = MAX_SOCKADDR_LEN; vu_alloc_local_arg(addraddr, addr, *addrlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_getsockname)(sfd, addr, addrlen, private); if (ret_value < 0) sd->ret_value = -errno; else { vu_poke_arg(addraddr, addr, *addrlen, nested); vu_poke_arg(paddrlen, addrlen, sizeof(socklen_t), nested); sd->ret_value = ret_value; } } } void wi_getpeername(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t addraddr = sd->syscall_args[1]; void *addr; uintptr_t paddrlen = sd->syscall_args[2]; socklen_t *addrlen; vu_alloc_peek_local_arg(paddrlen, addrlen, sizeof(socklen_t), nested); if (*addrlen > MAX_SOCKADDR_LEN) *addrlen = MAX_SOCKADDR_LEN; vu_alloc_local_arg(addraddr, addr, *addrlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_getpeername)(sfd, addr, addrlen, private); if (ret_value < 0) sd->ret_value = -errno; else { vu_poke_arg(addraddr, addr, *addrlen, nested); vu_poke_arg(paddrlen, addrlen, sizeof(socklen_t), nested); sd->ret_value = ret_value; } } } static int get_send_recv_flags(struct syscall_descriptor_t *sd) { int syscall_number = sd->syscall_number; switch (syscall_number) { case __NR_sendto: case __NR_recvfrom: case __NR_sendmmsg: case __NR_recvmmsg: return sd->syscall_args[3]; case __NR_sendmsg: case __NR_recvmsg: return sd->syscall_args[2]; default: return 0; } } /* sendto, send, sendmsg, sendmmsg */ /* The call is processed only in the out phase when surely it won't block. * wrap in and wrap during functions are used to wait until the socket is ready.*/ void wo_sendto(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd); void wi_sendto(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { if (!nested && (get_send_recv_flags(sd) & MSG_DONTWAIT) == 0) { int fd = sd->syscall_args[0]; struct slowcall *sc = vu_slowcall_in(ht, fd, EPOLLOUT, nested); if (sc != NULL) { sd->inout = sc; sd->action = BLOCKIT; return; } } wo_sendto(ht, sd); } } void wd_sendto(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct slowcall *sc = sd->inout; sd->waiting_pid = vu_slowcall_during(sc); } void _wo_sendto(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { /* standard args */ int syscall_number = sd->syscall_number; int nested = sd->extra->nested; /* args */ int fd = sd->syscall_args[0]; uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; int flags = (syscall_number != __NR_write) ? sd->syscall_args[3] : 0; uintptr_t dest_addraddr = (syscall_number == __NR_sendto) ? sd->syscall_args[4] : 0; socklen_t addrlen = (syscall_number == __NR_sendto) ? sd->syscall_args[5] : 0; void *dest_addr = NULL; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); void *buf; ssize_t ret_value; vu_alloc_peek_arg(addr, buf, bufsize, nested); if (addrlen > MAX_SOCKADDR_LEN) addrlen = MAX_SOCKADDR_LEN; vu_alloc_peek_local_arg(dest_addraddr, dest_addr, addrlen, nested); if (dest_addraddr == 0) dest_addr = NULL; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_sendto)(sfd, buf, bufsize, flags, dest_addr, addrlen, NULL, 0, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_arg(buf, nested); } void _wo_sendmsg(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { /* standard args */ int nested = sd->extra->nested; /* args */ int fd = sd->syscall_args[0]; uintptr_t msgaddr = sd->syscall_args[1]; int flags = sd->syscall_args[2]; struct msghdr *msg; uintptr_t dest_addraddr; void *dest_addr = NULL; uintptr_t iovaddr; struct iovec *iov; uintptr_t controladdr; void *control = NULL; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); void *buf; size_t bufsize; ssize_t ret_value; vu_alloc_peek_local_arg(msgaddr, msg, sizeof(struct msghdr), nested); if (msg->msg_namelen > MAX_SOCKADDR_LEN) msg->msg_namelen = MAX_SOCKADDR_LEN; dest_addraddr = (uintptr_t) msg->msg_name; vu_alloc_peek_local_arg(dest_addraddr, dest_addr, msg->msg_namelen, nested); if (dest_addraddr == 0) dest_addr = NULL; iovaddr = (uintptr_t) msg->msg_iov; vu_alloc_peek_iov_arg(iovaddr, iov, msg->msg_iovlen, buf, bufsize, nested); controladdr = (uintptr_t) msg->msg_control; vu_alloc_peek_arg(controladdr, control, msg->msg_controllen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_sendto)(sfd, buf, bufsize, flags, dest_addr, msg->msg_namelen, control, msg->msg_controllen, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_iov_arg(iov, buf, nested); vu_free_arg(control, nested); } #define EXP_SEMDMMSG void _wo_sendmmsg(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { #ifdef EXP_SEMDMMSG /* standard args */ int nested = sd->extra->nested; /* args */ int fd = sd->syscall_args[0]; uintptr_t msgvecaddr = sd->syscall_args[1]; int len = sd->syscall_args[2] & 1023; int flags = sd->syscall_args[3]; struct mmsghdr *msgvec; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int mret_value; ssize_t ret_value = -EINVAL; vu_alloc_peek_local_arg(msgvecaddr, msgvec, sizeof(struct mmsghdr) * len, nested); sd->action = SKIPIT; for (mret_value = 0; mret_value < len; mret_value++) { struct msghdr *msg = &msgvec[mret_value].msg_hdr; uintptr_t dest_addraddr; void *dest_addr = NULL; uintptr_t iovaddr; struct iovec *iov; uintptr_t controladdr; void *control = NULL; void *buf; size_t bufsize; dest_addraddr = (uintptr_t) msg->msg_name; vu_alloc_peek_local_arg(dest_addraddr, dest_addr, msg->msg_namelen, nested); if (dest_addraddr == 0) dest_addr = NULL; iovaddr = (uintptr_t) msg->msg_iov; vu_alloc_peek_iov_arg(iovaddr, iov, msg->msg_iovlen, buf, bufsize, nested); controladdr = (uintptr_t) msg->msg_control; vu_alloc_peek_arg(controladdr, control, msg->msg_controllen, nested); ret_value = service_syscall(ht, __VU_sendto)(sfd, buf, bufsize, flags, dest_addr, msg->msg_namelen, control, msg->msg_controllen, private); if (ret_value >= 0) msgvec[mret_value].msg_len = ret_value; if (ret_value <= 0) break; } if (mret_value == 0 && ret_value < 0) sd->ret_value = -errno; else { vu_poke_arg(msgvecaddr, msgvec, sizeof(struct mmsghdr) * len, nested); sd->ret_value = mret_value; } #else sd->ret_value = -ENOSYS; sd->action = SKIPIT; #endif } void wo_sendto(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; struct slowcall *sc = sd->inout; /* standard args */ int syscall_number = sd->syscall_number; int fd = sd->syscall_args[0]; if (sc != NULL) { vu_slowcall_out(sc, ht, fd, EPOLLOUT, nested); if (sd->waiting_pid != 0) { sd->ret_value = -EINTR; sd->action = SKIPIT; return; } } switch (syscall_number) { case __NR_write: case __NR_sendto: _wo_sendto(ht, sd); break; case __NR_sendmsg: _wo_sendmsg(ht, sd); break; case __NR_sendmmsg: _wo_sendmmsg(ht, sd); break; } } /* recvfrom, recv, recvmsg, recvmmsg */ /* The call is processed only in the out phase when surely it won't block. * wrap in and wrap during functions are used to wait until the socket is ready.*/ void wo_recvfrom(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd); void wi_recvfrom(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { if (!nested && (get_send_recv_flags(sd) & MSG_DONTWAIT) == 0) { int fd = sd->syscall_args[0]; struct slowcall *sc = vu_slowcall_in(ht, fd, EPOLLIN, nested); if (sc != NULL) { sd->inout = sc; sd->action = BLOCKIT; return; } } wo_recvfrom(ht, sd); } } void wd_recvfrom(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { struct slowcall *sc = sd->inout; sd->waiting_pid = vu_slowcall_during(sc); } void _wo_recvfrom(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { /* standard args */ int syscall_number = sd->syscall_number; int nested = sd->extra->nested; /* args */ int fd = sd->syscall_args[0]; uintptr_t addr = sd->syscall_args[1]; size_t bufsize = sd->syscall_args[2]; int flags = (syscall_number != __NR_read) ? sd->syscall_args[3] : 0; uintptr_t src_addraddr = (syscall_number == __NR_recvfrom) ? sd->syscall_args[4] : 0; uintptr_t paddrlen = (syscall_number == __NR_recvfrom) ? sd->syscall_args[5] : 0; socklen_t *addrlen; void *src_addr = NULL; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); void *buf; ssize_t ret_value; vu_alloc_arg(addr, buf, bufsize, nested); vu_alloc_peek_local_arg(paddrlen, addrlen, sizeof(socklen_t), nested); if (*addrlen > MAX_SOCKADDR_LEN) *addrlen = MAX_SOCKADDR_LEN; vu_alloc_local_arg(src_addraddr, src_addr, *addrlen, nested); if (src_addraddr == 0) src_addr = NULL; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_recvfrom)(sfd, buf, bufsize, flags, src_addr, addrlen, NULL, 0, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; vu_poke_arg(addr, buf, ret_value, nested); vu_poke_arg(src_addraddr, src_addr, *addrlen, nested); vu_poke_arg(paddrlen, addrlen, sizeof(socklen_t), nested); } vu_free_arg(buf, nested); } void _wo_recvmsg(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { /* standard args */ int nested = sd->extra->nested; /* args */ int fd = sd->syscall_args[0]; uintptr_t msgaddr = sd->syscall_args[1]; int flags = sd->syscall_args[2]; struct msghdr *msg; uintptr_t src_addraddr; void *src_addr = NULL; uintptr_t iovaddr; struct iovec *iov; uintptr_t controladdr; void *control = NULL; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); void *buf; size_t bufsize; ssize_t ret_value; vu_alloc_peek_local_arg(msgaddr, msg, sizeof(struct msghdr), nested); if (msg->msg_namelen > MAX_SOCKADDR_LEN) msg->msg_namelen = MAX_SOCKADDR_LEN; src_addraddr = (uintptr_t) msg->msg_name; vu_alloc_local_arg(src_addraddr, src_addr, msg->msg_namelen, nested); if (src_addraddr == 0) src_addr = NULL; iovaddr = (uintptr_t) msg->msg_iov; vu_alloc_iov_arg(iovaddr, iov, msg->msg_iovlen, buf, bufsize, nested); controladdr = (uintptr_t) msg->msg_control; vu_alloc_arg(controladdr, control, msg->msg_controllen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_recvfrom)(sfd, buf, bufsize, flags, src_addr, &msg->msg_namelen, control, &msg->msg_controllen, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; vu_poke_iov_arg(iovaddr, iov, msg->msg_iovlen, buf, ret_value, nested); vu_poke_arg(controladdr, control, msg->msg_controllen, nested); vu_poke_arg(src_addraddr, src_addr, msg->msg_namelen, nested); vu_poke_arg(msgaddr, msg, sizeof(struct msghdr), nested); } vu_free_iov_arg(iov, buf, nested); vu_free_arg(control, nested); } void _wo_recvmmsg(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } void wo_recvfrom(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; struct slowcall *sc = sd->inout; /* standard args */ int syscall_number = sd->syscall_number; int fd = sd->syscall_args[0]; if (sc != NULL) { vu_slowcall_out(sc, ht, fd, EPOLLIN, nested); if (sd->waiting_pid != 0) { sd->ret_value = -EINTR; sd->action = SKIPIT; return; } } switch (syscall_number) { case __NR_read: case __NR_recvfrom: _wo_recvfrom(ht, sd); break; case __NR_recvmsg: _wo_recvmsg(ht, sd); break; case __NR_recvmmsg: _wo_recvmmsg(ht, sd); break; } } void wi_shutdown(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); int how = sd->syscall_args[1]; sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_shutdown)(sfd, how, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } } void wi_setsockopt(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int level = sd->syscall_args[1]; int optname = sd->syscall_args[2]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t optvaladdr = sd->syscall_args[3]; socklen_t optlen = sd->syscall_args[4]; void *optval; vu_alloc_peek_arg(optvaladdr, optval, optlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_setsockopt)(sfd, level, optname, optval, optlen, private); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; vu_free_arg(optval, nested); } } void wi_getsockopt(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { int ret_value; int fd = sd->syscall_args[0]; int level = sd->syscall_args[1]; int optname = sd->syscall_args[2]; int nested = sd->extra->nested; void *private = NULL; int sfd = vu_fd_get_sfd(fd, &private, nested); uintptr_t optvaladdr = sd->syscall_args[3]; uintptr_t optlenaddr = sd->syscall_args[4]; void *optval; socklen_t *optlen; vu_alloc_peek_local_arg(optlenaddr, optlen, sizeof(socklen_t), nested); vu_alloc_arg(optvaladdr, optval, *optlen, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_getsockopt)(sfd, level, optname, optval, optlen, private); if (ret_value < 0) sd->ret_value = -errno; else { sd->ret_value = ret_value; vu_poke_arg(optvaladdr, optval, *optlen, nested); } vu_free_arg(optval, nested); } } __attribute__((constructor)) static void init(void) { vu_fnode_set_close_upcall(S_IFSOCK, socket_close_upcall); multiplex_read_wrappers(S_IFSOCK, wi_recvfrom, wd_recvfrom, wo_recvfrom); multiplex_write_wrappers(S_IFSOCK, wi_sendto, wd_sendto, wo_sendto); } vuos-0.9.2/umvu/src/vu_wrap_time.c000066400000000000000000000117551476575172100171760ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_clock_gettime(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { long ret_value; switch (sd->syscall_number) { case __NR_clock_gettime: { clockid_t clk_id = sd->syscall_args[0]; uintptr_t tpaddr = sd->syscall_args[1]; if (tpaddr == 0) ret_value = -EFAULT; else { struct timespec *tp; vu_alloc_local_arg(tpaddr, tp, sizeof(*tp), nested); ret_value = service_syscall(ht, __VU_clock_gettime)(clk_id, tp); if (ret_value == 0) vu_poke_arg(tpaddr, tp, sizeof(*tp), nested); else ret_value = -errno; } } break; case __NR_gettimeofday: { /* timezone is obsolete. ignored here */ uintptr_t tvaddr = sd->syscall_args[0]; if (tvaddr == 0) ret_value = -EFAULT; else { struct timespec tp; struct timeval *tv; vu_alloc_local_arg(tvaddr, tv, sizeof(*tv), nested); ret_value = service_syscall(ht, __VU_clock_gettime)(CLOCK_REALTIME, &tp); tv->tv_sec = tp.tv_sec; tv->tv_usec = tp.tv_nsec / 1000; if (ret_value == 0) vu_poke_arg(tvaddr, tv, sizeof(*tv), nested); else ret_value = -errno; } } break; case __NR_time: { uintptr_t timeaddr = sd->syscall_args[0]; struct timespec tp; ret_value = service_syscall(ht, __VU_clock_gettime)(CLOCK_REALTIME, &tp); if (ret_value == 0) { ret_value = tp.tv_sec; if (timeaddr != 0) { time_t *now; vu_alloc_local_arg(timeaddr, now, sizeof(*now), nested); *now = tp.tv_sec; vu_poke_arg(timeaddr, now, sizeof(*now), nested); } } else ret_value = -errno; } break; default: default_nosys(sd); } if (ret_value != -EINTR) { sd->action = SKIPIT; sd->ret_value = ret_value; } } } void wi_clock_settime(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { long ret_value; switch (sd->syscall_number) { case __NR_clock_settime: { clockid_t clk_id = sd->syscall_args[0]; uintptr_t tpaddr = sd->syscall_args[1]; if (tpaddr == 0) ret_value = -EFAULT; else { struct timespec *tp; vu_alloc_peek_local_arg(tpaddr, tp, sizeof(*tp), nested); ret_value = service_syscall(ht, __VU_clock_settime)(clk_id, tp); if (ret_value < 0) ret_value = -errno; } } break; case __NR_settimeofday: { /* timezone is obsolete. ignored here */ uintptr_t tvaddr = sd->syscall_args[0]; if (tvaddr == 0) ret_value = -EFAULT; else { struct timespec tp; struct timeval *tv; vu_alloc_peek_local_arg(tvaddr, tv, sizeof(*tv), nested); tp.tv_sec = tv->tv_sec; tp.tv_nsec = tv->tv_usec * 1000; ret_value = service_syscall(ht, __VU_clock_settime)(CLOCK_REALTIME, &tp); if (ret_value < 0) ret_value = -errno; } } break; default: default_nosys(sd); } if (ret_value != -EINTR) { sd->action = SKIPIT; sd->ret_value = ret_value; } } } void wi_clock_getres(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { long ret_value; uintptr_t tpaddr = sd->syscall_args[1]; if (tpaddr == 0) ret_value = -EFAULT; else { clockid_t clk_id = sd->syscall_args[0]; uintptr_t tpaddr = sd->syscall_args[1]; if (tpaddr == 0) ret_value = -EFAULT; else { struct timespec *tp; vu_alloc_local_arg(tpaddr, tp, sizeof(*tp), nested); ret_value = service_syscall(ht, __VU_clock_getres)(clk_id, tp); if (ret_value == 0) vu_poke_arg(tpaddr, tp, sizeof(*tp), nested); else ret_value = -errno; } } if (ret_value == -EINTR) sd->action = SKIPIT; else sd->ret_value = ret_value; } } vuos-0.9.2/umvu/src/vu_wrap_uid_gid.c000066400000000000000000000332271476575172100176420ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_getresfuid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int syscall_number = sd->syscall_number; uid_t current_ruid, current_euid, current_suid, current_fsuid; if (ht) { service_syscall(ht, __VU_getresfuid)(¤t_ruid, ¤t_euid, ¤t_suid, ¤t_fsuid, vuht_get_private_data(ht)); } else { vu_uidgid_getresfuid(¤t_ruid, ¤t_euid, ¤t_suid, ¤t_fsuid); } switch (syscall_number) { case __NR_getuid: sd->ret_value = current_ruid; break; case __NR_geteuid: sd->ret_value = current_euid; break; case __NR_getresuid: { uintptr_t addr_ruid = sd->syscall_args[0]; uintptr_t addr_euid = sd->syscall_args[1]; uintptr_t addr_suid = sd->syscall_args[2]; if (addr_ruid != 0) { uid_t *pruid; vu_alloc_local_arg(addr_ruid, pruid, sizeof(uid_t), nested); *pruid = current_ruid; vu_poke_arg(addr_ruid, pruid, sizeof(uid_t), nested); } if (addr_euid != 0) { uid_t *peuid; vu_alloc_local_arg(addr_euid, peuid, sizeof(uid_t), nested); *peuid = current_euid; vu_poke_arg(addr_euid, peuid, sizeof(uid_t), nested); } if (addr_suid != 0) { uid_t *psuid; vu_alloc_local_arg(addr_suid, psuid, sizeof(uid_t), nested); *psuid = current_suid; vu_poke_arg(addr_suid, psuid, sizeof(uid_t), nested); } sd->ret_value = 0; } break; default: default_nosys(sd); } sd->action = SKIPIT; } void wi_getresfgid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; int syscall_number = sd->syscall_number; gid_t current_rgid, current_egid, current_sgid, current_fsgid; if (ht) { service_syscall(ht, __VU_getresfgid)(¤t_rgid, ¤t_egid, ¤t_sgid, ¤t_fsgid, vuht_get_private_data(ht)); } else { vu_uidgid_getresfgid(¤t_rgid, ¤t_egid, ¤t_sgid, ¤t_fsgid); } switch (syscall_number) { case __NR_getgid: sd->ret_value = current_rgid; break; case __NR_getegid: sd->ret_value = current_egid; break; case __NR_getresgid: { uintptr_t addr_rgid = sd->syscall_args[0]; uintptr_t addr_egid = sd->syscall_args[1]; uintptr_t addr_sgid = sd->syscall_args[2]; if (addr_rgid != 0) { gid_t *prgid; vu_alloc_local_arg(addr_rgid, prgid, sizeof(gid_t), nested); *prgid = current_rgid; vu_poke_arg(addr_rgid, prgid, sizeof(gid_t), nested); } if (addr_egid != 0) { gid_t *pegid; vu_alloc_local_arg(addr_egid, pegid, sizeof(gid_t), nested); *pegid = current_egid; vu_poke_arg(addr_egid, pegid, sizeof(gid_t), nested); } if (addr_sgid != 0) { gid_t *psgid; vu_alloc_local_arg(addr_sgid, psgid, sizeof(gid_t), nested); *psgid = current_sgid; vu_poke_arg(addr_sgid, psgid, sizeof(gid_t), nested); } sd->ret_value = 0; } break; default: default_nosys(sd); } sd->action = SKIPIT; } void wi_getgroups(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int ret_value; int nested = sd->extra->nested; int size = sd->syscall_args[0]; uintptr_t listaddr = sd->syscall_args[1]; gid_t *list; vu_alloc_arg(listaddr, list, size * sizeof(gid_t), nested); if (ht) { ret_value = service_syscall(ht, __VU_getgroups)(size, list, vuht_get_private_data(ht)); } else { ret_value = vu_uidgid_getgroups(size, list); if (ret_value < 0) errno = EINVAL; } if (ret_value > 0) vu_poke_arg(listaddr, list, ret_value * sizeof(gid_t), nested); vu_free_arg(list, nested); sd->action = SKIPIT; if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; } static void wi_setresuid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { int syscall_number = sd->syscall_number; uid_t ruid, euid, suid, fsuid; uid_t new_ruid, new_euid, new_suid; int ret_value = -1; service_syscall(ht, __VU_getresfuid) (&ruid, &euid, &suid, &fsuid, vuht_get_private_data(ht)); switch (syscall_number) { case __NR_setuid: new_ruid = -1; new_euid = sd->syscall_args[0]; new_suid = -1; /* If the caller is root the set both ,ruid and suid. */ if (new_euid != (uid_t) -1 && euid == 0) new_ruid = new_suid = new_euid; break; case __NR_setreuid: new_ruid = sd->syscall_args[0]; new_euid = sd->syscall_args[1]; new_suid = -1; /* If the real user ID is set (i.e., ruid is not -1) or the effective user ID is set to a value not equal to the previous real user ID, the saved set-user-ID will be set to the new effec- tive user ID. */ if (new_ruid != (uid_t) -1 || (new_euid != (uid_t) -1 && new_euid != ruid)) new_suid = (new_euid == (uid_t) -1) ? euid : new_euid; break; case __NR_setresuid: new_ruid = sd->syscall_args[0]; new_euid = sd->syscall_args[1]; new_suid = sd->syscall_args[2]; break; default: default_nosys(sd); } if (new_ruid != (uid_t) -1) ruid = new_ruid; if (new_euid != (uid_t) -1 && euid != new_euid) euid = fsuid = new_euid; if (new_suid != (uid_t) -1) suid = new_suid; if (syscall_number == __NR_setresuid) fsuid = euid; ret_value = service_syscall(ht, __VU_setresfuid)(ruid, euid, suid, fsuid, vuht_get_private_data(ht)); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; sd->action = SKIPIT; } else if (nested) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } else { // !ht && !nested sd->action = DOIT_CB_AFTER; } } static void wi_setresgid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { int syscall_number = sd->syscall_number; uid_t euid; gid_t rgid, egid, sgid, fsgid; gid_t new_rgid, new_egid, new_sgid; int ret_value = -1; service_syscall(ht, __VU_getresfuid) (NULL, &euid, NULL, NULL, vuht_get_private_data(ht)); service_syscall(ht, __VU_getresfgid) (&rgid, &egid, &sgid, &fsgid, vuht_get_private_data(ht)); switch (syscall_number) { case __NR_setgid: new_rgid = -1; new_egid = sd->syscall_args[0]; new_sgid = -1; if (new_egid != (gid_t) -1 && euid == 0) new_rgid = new_sgid = new_egid; break; case __NR_setregid: new_rgid = sd->syscall_args[0]; new_egid = sd->syscall_args[1]; new_sgid = -1; /* If the real user ID is set (i.e., rgid is not -1) or the effective user ID is set to a value not equal to the previous real user ID, the saved set-user-ID will be set to the new effec- tive user ID. */ if (new_rgid != (gid_t) -1 || (new_egid != (gid_t) -1 && new_egid != rgid)) new_sgid = (new_egid == (gid_t) -1) ? egid : new_egid; break; case __NR_setresgid: new_rgid = sd->syscall_args[0]; new_egid = sd->syscall_args[1]; new_sgid = sd->syscall_args[2]; break; default: default_nosys(sd); } if (new_rgid != (gid_t) -1) rgid = new_rgid; if (new_egid != (gid_t) -1 && egid != new_egid) egid = fsgid = new_egid; if (new_sgid != (gid_t) -1) sgid = new_sgid; if (syscall_number == __NR_setresgid) fsgid = egid; ret_value = service_syscall(ht, __VU_setresfgid)(rgid, egid, sgid, fsgid, vuht_get_private_data(ht)); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; sd->action = SKIPIT; } else if (nested) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } else { // !ht && !nested sd->action = DOIT_CB_AFTER; } } static void wi_setfsuid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; uid_t new_fsuid = sd->syscall_args[0]; if (ht) { uid_t ruid, euid, suid, fsuid; service_syscall(ht, __VU_getresfuid)(&ruid, &euid, &suid, &fsuid, vuht_get_private_data(ht)); sd->ret_value = fsuid; if (new_fsuid != (uid_t ) -1) service_syscall(ht, __VU_setresfuid)(ruid, euid, suid, new_fsuid, vuht_get_private_data(ht)); sd->action = SKIPIT; } else if (nested) { if (new_fsuid != (uid_t ) -1) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } else { uid_t ruid, euid, suid, fsuid; vu_uidgid_getresfuid(&ruid, &euid, &suid, &fsuid); sd->ret_value = fsuid; } } else { // !ht && !nested sd->action = DOIT_CB_AFTER; } } static void wi_setfsgid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; gid_t new_fsgid = sd->syscall_args[0]; if (ht) { gid_t rgid, egid, sgid, fsgid; service_syscall(ht, __VU_getresfgid)(&rgid, &egid, &sgid, &fsgid, vuht_get_private_data(ht)); sd->ret_value = fsgid; if (new_fsgid != (gid_t ) -1) service_syscall(ht, __VU_setresfgid)(rgid, egid, sgid, new_fsgid, vuht_get_private_data(ht)); sd->action = SKIPIT; } else if (nested) { gid_t new_fsgid = sd->syscall_args[0]; if (new_fsgid != (gid_t ) -1) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } else { gid_t rgid, egid, sgid, fsgid; vu_uidgid_getresfgid(&rgid, &egid, &sgid, &fsgid); sd->ret_value = fsgid; } } else { // !ht && !nested sd->action = DOIT_CB_AFTER; } } /* in this way modules can provide only setresfuid and setresfgid */ void wi_setresfuid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int syscall_number = sd->syscall_number; if (syscall_number == __NR_setfsuid) wi_setfsuid(ht, sd); else wi_setresuid(ht, sd); } void wi_setresfgid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int syscall_number = sd->syscall_number; if (syscall_number == __NR_setfsgid) wi_setfsgid(ht, sd); else wi_setresgid(ht, sd); } void wo_setresfuid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; if (sd->ret_value == 0) { /* if the real system call succeeded, update vu_uidgid */ int syscall_number = sd->syscall_number; uid_t ruid, euid, suid, fsuid, newfsuid; vu_uidgid_getresfuid(&ruid, &euid, &suid, &fsuid); switch (syscall_number) { case __NR_setuid: if (euid == 0) euid = ruid = suid = fsuid = sd->syscall_args[0]; else euid = sd->syscall_args[0]; break; case __NR_setreuid: ruid = sd->syscall_args[0]; euid = sd->syscall_args[1]; break; case __NR_setresuid: ruid = sd->syscall_args[0]; euid = sd->syscall_args[1]; suid = sd->syscall_args[2]; break; case __NR_setfsuid: newfsuid = sd->syscall_args[0]; if (euid == 0 || newfsuid == ruid || newfsuid == euid || newfsuid == suid || newfsuid == fsuid) fsuid = newfsuid; break; } vu_uidgid_setresfuid(ruid, euid, suid, fsuid); } } void wo_setresfgid(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; if (sd->ret_value == 0) { /* if the real system call succeeded, update vu_uidgid */ int syscall_number = sd->syscall_number; uid_t euid; gid_t rgid, egid, sgid, fsgid, newfsgid; vu_uidgid_getresfuid(NULL, &euid, NULL, NULL); vu_uidgid_getresfgid(&rgid, &egid, &sgid, &fsgid); switch (syscall_number) { case __NR_setgid: if (euid == 0) egid = rgid = sgid = fsgid = sd->syscall_args[0]; else egid = sd->syscall_args[0]; break; case __NR_setregid: rgid = sd->syscall_args[0]; egid = sd->syscall_args[1]; break; case __NR_setresgid: rgid = sd->syscall_args[0]; egid = sd->syscall_args[1]; sgid = sd->syscall_args[2]; break; case __NR_setfsgid: newfsgid = sd->syscall_args[0]; if (egid == 0 || newfsgid == rgid || newfsgid == egid || newfsgid == sgid || newfsgid == fsgid) fsgid = newfsgid; break; } vu_uidgid_setresfgid(rgid, egid, sgid, fsgid); } } void wi_setgroups(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { int nested = sd->extra->nested; if (ht) { int ret_value; int size = sd->syscall_args[0]; uintptr_t listaddr = sd->syscall_args[1]; gid_t *list; vu_alloc_peek_arg(listaddr, list, size * sizeof(gid_t), nested); ret_value = service_syscall(ht, __VU_setgroups)(size, list, vuht_get_private_data(ht)); vu_free_arg(list, nested); if (ret_value < 0) sd->ret_value = -errno; else sd->ret_value = ret_value; sd->action = SKIPIT; } else if (nested) { sd->ret_value = -ENOSYS; sd->action = SKIPIT; } else { // !ht && !nested sd->action = DOIT_CB_AFTER; } } void wo_setgroups(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { sd->ret_value = sd->orig_ret_value; if (sd->ret_value == 0) { /* if the real system call succeeded, update vu_uidgid */ int size = sd->syscall_args[0]; uintptr_t listaddr = sd->syscall_args[1]; gid_t *list; vu_alloc_peek_arg(listaddr, list, size * sizeof(gid_t), VU_NOT_NESTED); vu_uidgid_setgroups(size, list); vu_free_arg(list, VU_NOT_NESTED); } } vuos-0.9.2/umvu/src/vu_wrap_xattr.c000066400000000000000000000116341476575172100173760ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2017 Renzo Davoli , Antonio Cardace * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void wi_lgetxattr(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int ret_value; int syscall_number = sd->syscall_number; /* args */ uintptr_t nameaddr = sd->syscall_args[1]; uintptr_t valueaddr = sd->syscall_args[2]; size_t size = sd->syscall_args[3]; char *name; char *value = NULL; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_fgetxattr: sfd = vu_fd_get_sfd(sfd, &private, nested); break; } vu_alloc_peek_local_strarg(nameaddr, name, PATH_MAX, nested); if (valueaddr > 0) vu_alloc_arg(valueaddr, value, size, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_lgetxattr)(sd->extra->mpath, name, value, size, sfd, private); if (ret_value < 0) sd->ret_value = (errno == ENOSYS) ? -ENOTSUP : -errno; else { sd->ret_value = ret_value; if (ret_value > 0 && valueaddr > 0) vu_poke_arg(valueaddr, value, ret_value, nested); } vu_free_arg(value, nested); } } void wi_lsetxattr(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int ret_value; int syscall_number = sd->syscall_number; /* args */ uintptr_t nameaddr = sd->syscall_args[1]; uintptr_t valueaddr = sd->syscall_args[2]; size_t size = sd->syscall_args[3]; int flags = sd->syscall_args[4]; char *name; char *value; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_fsetxattr: sfd = vu_fd_get_sfd(sfd, &private, nested); break; } vu_alloc_peek_local_strarg(nameaddr, name, PATH_MAX, nested); vu_alloc_peek_arg(valueaddr, value, size, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_lsetxattr)(sd->extra->mpath, name, value, size, flags, sfd, private); if (ret_value < 0) sd->ret_value = (errno == ENOSYS) ? -ENOTSUP : -errno; else sd->ret_value = ret_value; vu_free_arg(value, nested); } } void wi_llistxattr(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int ret_value; int syscall_number = sd->syscall_number; /* args */ uintptr_t listaddr = sd->syscall_args[1]; size_t size = sd->syscall_args[2]; char *list = NULL; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_flistxattr: sfd = vu_fd_get_sfd(sfd, &private, nested); break; } if (listaddr > 0) vu_alloc_arg(listaddr, list, size, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_llistxattr)(sd->extra->mpath, list, size, sfd, private); if (ret_value < 0) sd->ret_value = (errno == ENOSYS) ? -ENOTSUP : -errno; else { sd->ret_value = ret_value; if (ret_value > 0 && listaddr > 0) vu_poke_arg(listaddr, list, ret_value, nested); } vu_free_arg(list, nested); } } void wi_lremovexattr(struct vuht_entry_t *ht, struct syscall_descriptor_t *sd) { if (ht) { /* standard args */ int nested = sd->extra->nested; int ret_value; int syscall_number = sd->syscall_number; /* args */ uintptr_t nameaddr = sd->syscall_args[1]; char *name; int sfd = -1; void *private = NULL; /* fetch args */ switch (syscall_number) { case __NR_fremovexattr: sfd = vu_fd_get_sfd(sfd, &private, nested); break; } vu_alloc_peek_local_strarg(nameaddr, name, PATH_MAX, nested); sd->action = SKIPIT; ret_value = service_syscall(ht, __VU_lremovexattr)(sd->extra->mpath, name, sfd, private); if (ret_value < 0) sd->ret_value = (errno == ENOSYS) ? -ENOTSUP : -errno; else sd->ret_value = ret_value; } } vuos-0.9.2/vu_syscalls.conf000066400000000000000000000113401476575172100157520ustar00rootroot00000000000000# Syscalls supported by UMView # syscall_list: choice, wrapIN, wrapDURING, wrapOUT # each syscall name has a parameter (/ followed by three octal digits) # APN: A is 1 if this is an "l" call (lstat, lchown... not following the symlinks) # A is 2 if this is an "at" call (openat, fstatat... using a path and a dirfd) # A is 3 for special cases e.g. at supporting AT_SYMLINK_NOFOLLOW) # P is the path argument (used by the choice fun, the dirfd arg in case of at calls) # N is the number of arguments # when the parameter is omitter it is 000. # e.g. fork (no parameter, zero args, no path) # write/3 (3 parameters, no path) # stat/12 (2 parameters, the first being the path) # lstat/112 (2 parameters, the first being the path, do not follow links) # mkdirat/213 (3 parameters, at, the first is dirfd) # faccessat/314 (4 parameters, the first is dirfd followed by the path + AT_SYMLINK_NOFOLLOW) null: NULL, NULL, NULL, NULL open/313, creat/12, openat/314: std, open, NULL, open close/1: std, close, NULL, close read/3, readv/3: std, read, read, read write/3, writev/3: std, write, write, write pread64/4, preadv/4, preadv2/5: std, pread, NULL, NULL pwrite64/4, pwritev/4, pwritev2/5: std, pwrite, NULL, NULL getdents64/3, getdents/3: std, getdents64, NULL, NULL fcntl/3, fcntl64/3: fd, fcntl, fcntl, fcntl lseek/3: std, lseek, NULL, NULL lstat/112, stat/12, fstatat/314, newfstatat/314, fstat/2: std, lstat, NULL, NULL access/12, faccessat/213, faccessat2/314: std, access, NULL, NULL readlink/113, readlinkat/314: std, readlink, NULL, NULL unlink/111, unlinkat/313: std, unlink, NULL, NULL truncate/12, ftruncate/2: std, truncate, NULL, NULL mkdir/12, mkdirat/213: std, mkdir, NULL, NULL rmdir/11: std, rmdir, NULL, NULL mknod/13, mknodat/214: std, mknod, NULL, NULL lchown/13, fchown/3, chown/13, fchownat/315: std, lchown, NULL, NULL chmod/12, fchmod/2, fchmodat/314, fchmodat2/314: std, chmod, NULL, NULL utimensat/314, utime/12, utimes/12, futimesat/213: utimensat, utimensat, NULL, NULL link/22, linkat/335: std, link, NULL, NULL symlink/22, symlinkat/223: std, symlink, NULL, NULL rename/22, renameat/234, renameat2/235: std, rename, NULL, NULL statfs/12, fstatfs/2: std, statfs, NULL, NULL mount/25: mount, mount, NULL, NULL umount2/312, umount/11: umount2, umount2, NULL, NULL lgetxattr/114, getxattr/14, fgetxattr/4: std, lgetxattr, NULL, NULL lsetxattr/115, setxattr/15, fsetxattr/5: std, lsetxattr, NULL, NULL llistxattr/113, listxattr/13, flistxattr/3: std, llistxattr, NULL, NULL lremovexattr/113, removexattr/13, fremovexattr/3: std, lremovexattr, NULL, NULL ioctl/3: ioctl, ioctl, NULL, NULL epoll_ctl/4: fd2, epoll_ctl, NULL, NULL setresfuid/4, setresuid/3, setuid/1, setreuid/2, setfsuid/1: sc, setresfuid, NULL, setresfuid setresfgid/4, setresgid/3, setgid/1, setregid/2, setfsgid/1: sc, setresfgid, NULL, setresfgid getresfuid/4, getresuid/3, getuid/1, geteuid/1: sc, getresfuid, NULL, NULL getresfgid/4, getresgid/3, getgid/1, getegid/1: sc, getresfgid, NULL, NULL setgroups/2: sc, setgroups, NULL, setgroups getgroups/2: sc, getgroups, NULL, NULL socket/3: socket, socket, NULL, socket bind/3: fd, bind, NULL, NULL connect/3: fd, connect, NULL, NULL listen/2: fd, listen, NULL, NULL accept4/4, accept/3: fd, accept4, accept4, accept4 getsockname/3: fd, getsockname, NULL, NULL getpeername/3: fd, getpeername, NULL, NULL sendto/5, sendmsg/3, sendmmsg/4: fd, sendto, sendto, sendto recvfrom/5, recvmsg/3, recvmmsg/4: fd, recvfrom, recvfrom, recvfrom shutdown/2: fd, shutdown, NULL, NULL setsockopt/5: fd, setsockopt, NULL, NULL getsockopt/5: fd, getsockopt, NULL, NULL capget/2: sc, capget, NULL, NULL capset/2: sc, capset, NULL, NULL clock_gettime/2, gettimeofday/2, time/1: sc, clock_gettime, NULL, NULL clock_settime/2, settimeofday/2: sc, clock_settime, NULL, NULL clock_getres/2: sc, clock_getres, NULL, NULL statx/315: std, statx, NULL, NULL BUILTIN execve/13, execveat/315: path, execve, NULL, execve dup3/3, dup2/2, dup/1: std, dup3, NULL, dup3 chdir/11, fchdir/1: std_nonest, chdir, NULL, chdir getcwd/1: NULL, getcwd, NULL, NULL umask/1: NULL, umask, NULL, NULL chroot/11: NULL, chroot, NULL, NULL mmap/6, mmap2/6: mmap, mmap, NULL, mmap munmap/2: NULL, mm_cb_after, NULL, munmap mremap/4: NULL, mm_cb_after, NULL, mremap msync/3: NULL, mm_cb_after, NULL, msync epoll_create1/1, epoll_create/1: NULL, epoll_create1, NULL, epoll_create1 epoll_wait/4, epoll_pwait5: NULL, epoll_wait, epoll_wait, epoll_wait poll/3, ppoll/4: NULL, poll, poll, poll select/5, pselect6/6: NULL, select, select, select # return ENOSYS clone3: NULL, clone3, NULL, NULL sendfile: NULL, sendfile, NULL, NULL copy_file_range: NULL, copy_file_range, NULL, NULL -insmod/2: NULL, insmod -rmmod/1: NULL, rmmod -lsmod/2: NULL, lsmod -vuctl/4: NULL, vuctl -msocket/14: msocket, msocket vuos-0.9.2/vubinfmt/000077500000000000000000000000001476575172100143675ustar00rootroot00000000000000vuos-0.9.2/vubinfmt/CMakeLists.txt000066400000000000000000000006451476575172100171340ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(VU_MOD_TARGET vubinfmt) file(GLOB_RECURSE VUBINFMT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUBINFMT_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} stropt vumod) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vubinfmt/vubinfmt.c000066400000000000000000000334711476575172100163750ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(vubinfmt) struct vu_module_t vu_module = { .name = "vubinfmt", .description = "vu binfmt_misc support" }; struct vubinfmt_entry_t { struct vubinfmt_entry_t *next; ino_t ino; uint8_t enabled; char type; uint8_t offset; uint8_t len; uint8_t flags; uint8_t *magic; uint8_t *mask; char *interpreter; char name[]; }; struct vubinfmt_t { pthread_mutex_t mutex; uint8_t enabled; ino_t next_ino; struct vubinfmt_entry_t *head; struct vuht_entry_t *path_ht; struct vuht_entry_t *binfmt_ht; }; static inline uint8_t encode_binfmt_flag(char c) { switch (c) { case 'P': return BINFMT_PRESERVE_ARGV0; case 'O': return BINFMT_OPEN_BINARY; case 'C': return BINFMT_CREDENTIALS; default: return 0; } } void dump_binfmt_flags(FILE *f, uint8_t flags) { if (flags & BINFMT_PRESERVE_ARGV0) fprintf(f,"P"); if (flags & BINFMT_OPEN_BINARY) fprintf(f,"0"); if (flags & BINFMT_CREDENTIALS) fprintf(f,"C"); } void hexdump(FILE *f, uint8_t *data, int len) { int i; for (i = 0; i < len; i++) fprintf(f, "%02x", data[i]); } int hexescinput(char *src, uint8_t *data, int len) { int i; if (data) { memset(data, 0xff, len); for (i = 0; *src != 0 && i < len; src++, i++) { if (src[0] == '\\' && src[1] == 'x' && src[2] != 0 && src[3] != 0) { src += 2; sscanf(src, "%2hhx", &data[i]); src ++; } else { data[i] = *src; } } } else { for (i = 0; *src != 0 && i < len; src++, i++) { if (src[0] == '\\' && src[1] == 'x' && src[2] != 0 && src[3] != 0) src += 3; } } return i; } #define F_NULL 0 #define F_NAME 1 #define F_TYPE 2 #define F_OFFSET 3 #define F_MAGIC 4 #define F_MASK 5 #define F_INTERPRETER 6 #define F_FLAGS 7 int check_register_consistency(char **tags) { char *flag; // binfmt line begin by : if (tags[F_NULL][0] != 0) return 0; // NAME, not empty, can't be "status" or "register if (tags[F_NAME][0] == 0) return 0; if (strcmp(tags[F_NAME], "status") == 0) return 0; if (strcmp(tags[F_NAME], "register") == 0) return 0; // '/' is not permitted in name if (strchr(tags[F_NAME], '/') != NULL) return 0; // TYPE, must be either "M" or "E" if ((tags[F_TYPE][0] != 'M' && tags[F_TYPE][0] != 'E') || tags[F_TYPE][1] != 0) return 0; // magic, not empty if (tags[F_MAGIC][0] == 0) return 0; // interpreter, not empty if (tags[F_INTERPRETER][0] == 0) return 0; // flags, check consistency //for (flag = tags[F_FLAGS]; flag && *flag; flag++) //printf("%c %d\n", *flag, encode_binfmt_flag(*flag)); for (flag = tags[F_FLAGS]; flag && *flag; flag++) if (encode_binfmt_flag(*flag) == 0) return 0; return 1; } struct vubinfmt_entry_t *vubinfmt_newentry(char *input) { int tagc = stroptx(input, "", ":", STROPTX_ALLOW_MULTIPLE_SEP, NULL, NULL, 0); if (tagc == 9) { char *tags[tagc]; char *args[tagc]; stroptx(input, "", ":", STROPTX_ALLOW_MULTIPLE_SEP, tags, args, input); if (check_register_consistency(tags)) { int namelen = strlen(tags[F_NAME]) + 1; int interplen = strlen(tags[F_INTERPRETER]) + 1; int magiclen = hexescinput(tags[F_MAGIC], NULL, BINFMTBUFLEN); long offset = strtol(tags[F_OFFSET], NULL, 0); if (magiclen + offset <= BINFMTBUFLEN) { char *flag; struct vubinfmt_entry_t *entry = malloc(sizeof(struct vubinfmt_entry_t) + namelen + interplen + 2 * magiclen); entry->enabled = 1; entry->type = tags[F_TYPE][0]; entry->interpreter = entry->name + namelen; entry->magic = (uint8_t *) (entry->interpreter + interplen); entry->mask = entry->magic + magiclen; snprintf(entry->name, namelen, "%s", tags[F_NAME]); snprintf(entry->interpreter, interplen, "%s", tags[F_INTERPRETER]); entry->offset = offset; entry->len = magiclen; hexescinput(tags[F_MAGIC], entry->magic, magiclen); hexescinput(tags[F_MASK], entry->mask, magiclen); for (flag = tags[F_FLAGS], entry->flags = 0; flag && *flag; flag++) entry->flags |= encode_binfmt_flag(*flag); //for (int i=0; ienabled)?"en":"dis"); fprintf(f,"interpreter %s\n", entry->interpreter); fprintf(f,"flags: "); dump_binfmt_flags(f, entry->flags); fprintf(f,"\n"); fprintf(f,"offset %u\n", entry->offset); fprintf(f,"magic "); hexdump(f, entry->magic, entry->len); fprintf(f,"\n"); if (entry->type == 'M') { fprintf(f,"mask "); hexdump(f, entry->mask, entry->len); fprintf(f,"\n"); } } static struct vubinfmt_entry_t *vubinfmt_search(const char *name, struct vubinfmt_entry_t *head) { struct vubinfmt_entry_t *scan; for (scan = head; scan != NULL; scan = scan->next) { if (strcmp(scan->name, name) == 0) break; } return scan; } static struct vubinfmt_entry_t *vubinfmt_del(const char *name, struct vubinfmt_entry_t *head) { struct vubinfmt_entry_t **pscan; struct vubinfmt_entry_t *scan; for (pscan = &head, scan = *pscan; scan != NULL; pscan = &scan->next, scan = *pscan) { if (strcmp(scan->name, name) == 0) { *pscan = scan->next; free(scan); } } return head; } static int vubinfmt_match(struct binfmt_req_t *req, struct vubinfmt_entry_t *head) { struct vubinfmt_entry_t *scan; for (scan = head; scan != NULL; scan = scan->next) { if (scan->enabled) { if (scan->type == 'M') { int i,j,diff; for (i = scan->offset, j = 0, diff = 0; i < BINFMTBUFLEN && j < scan->len && diff == 0; i++, j++) diff = (req->filehead[i] ^ scan->magic[j]) & scan->mask[j]; if (diff == 0) break; } else if (scan->type == 'E') { int suffixpos = strlen(req->path) - scan->len; if (suffixpos > 0 && req->path[suffixpos - 1] == '.' && strncmp(req->path + suffixpos, (const char *) scan->magic, scan->len)==0) break; } } } if (scan != NULL) { snprintf(req->filehead, BINFMTBUFLEN + 2, "#!%s", scan->interpreter); req->flags |= scan->flags; return 1; } else return 0; } static int vubinfmt_confirm(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vubinfmt_t *vubinfmt = vuht_get_private_data(ht); struct binfmt_req_t *req = arg; int retval; pthread_mutex_lock(&(vubinfmt->mutex)); if (vubinfmt->enabled) retval = vubinfmt_match(req, vubinfmt->head); else retval = 0; pthread_mutex_unlock(&(vubinfmt->mutex)); return retval; } int vu_vubinfmt_root_upcall(int tag, FILE *f, int openflags, void *pseudoprivate) { struct vubinfmt_t *vubinfmt = pseudoprivate; if (tag == PSEUDOFILE_LOAD_DIRENTS) { struct vubinfmt_entry_t *scan; pseudofile_filldir(f, ".", 2, DT_DIR); pseudofile_filldir(f, "..", 2, DT_DIR); pseudofile_filldir(f, "status", 3, DT_REG); pseudofile_filldir(f, "register", 4, DT_REG); for (scan = vubinfmt->head; scan != NULL; scan = scan->next) pseudofile_filldir(f, scan->name, scan->ino, DT_REG); } return 0; } int vu_vubinfmt_status_upcall(int tag, FILE *f, int openflags, void *pseudoprivate) { struct vubinfmt_t *vubinfmt = pseudoprivate; if (tag == PSEUDOFILE_LOAD_CONTENTS) { fprintf(f, "%sbled\n", vubinfmt->enabled == 0 ? "disa" : "ena"); } if (tag == PSEUDOFILE_STORE_CLOSE) { int value; int valid = fscanf(f, "%d", &value); if (valid) { switch (value) { case 0: case 1: vubinfmt->enabled = value; break; case -1: while (vubinfmt->head != NULL) vubinfmt->head = vubinfmt_del(vubinfmt->head->name, vubinfmt->head); break; } } } return 0; } int vu_vubinfmt_register_upcall(int tag, FILE *f, int openflags, void *pseudoprivate) { struct vubinfmt_t *vubinfmt = pseudoprivate; if (tag == PSEUDOFILE_STORE_CLOSE) { char inbuf[BINFMTLINELEN + 1]; size_t inbuflen = fread(inbuf, 1, BINFMTLINELEN, f); struct vubinfmt_entry_t *new; if (inbuf[inbuflen - 1] == '\n') inbuflen--; inbuf[inbuflen] = 0; new = vubinfmt_newentry(inbuf); if (new) { new->ino = vubinfmt->next_ino++; vubinfmt->head = vubinfmt_del(new->name, vubinfmt->head); new->next = vubinfmt->head; vubinfmt->head = new; } } return 0; } int vu_binfmt_entry_upcall (int tag, FILE *f, int openflags, void *pseudoprivate) { struct vubinfmt_entry_t *this = pseudoprivate; if (tag == PSEUDOFILE_LOAD_CONTENTS) { vubinfmt_show(f, this); } if (tag == PSEUDOFILE_STORE_CLOSE && (openflags & O_ACCMODE) != O_RDONLY) { int value; int valid = fscanf(f, "%d", &value); if (valid) { switch (value) { case 0: case 1: this->enabled = value; break; case -1: { struct vuht_entry_t *ht = vu_mod_getht(); struct vubinfmt_t *vubinfmt = vuht_get_private_data(ht); vubinfmt->head = vubinfmt_del(this->name, vubinfmt->head); } break; } } } return 0; } int vu_vubinfmt_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *private) { struct vuht_entry_t *ht = vu_mod_getht(); struct vubinfmt_t *vubinfmt = vuht_get_private_data(ht); int retval = 0; memset(buf, 0, sizeof(struct vu_stat)); pthread_mutex_lock(&(vubinfmt->mutex)); pathname++; switch(strcase(pathname)) { case 0: buf->st_mode = S_IFDIR | 0755; buf->st_ino = 2; break; case (STRCASE(s,t,a,t,u,s)): buf->st_mode = S_IFREG | 0644; buf->st_ino = 3; break; case (STRCASE(r,e,g,i,s,t,e,r)): buf->st_mode = S_IFREG | 0200; buf->st_ino = 4; break; default: { struct vubinfmt_entry_t *this; this = vubinfmt_search(pathname, vubinfmt->head); if (this) { buf->st_mode = S_IFREG | 0644; buf->st_ino = this->ino; } else { errno = ENOENT; retval = -1; } } break; } pthread_mutex_unlock(&(vubinfmt->mutex)); return retval; } int vu_vubinfmt_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); struct vubinfmt_t *vubinfmt = vuht_get_private_data(ht); int retval = 0; pthread_mutex_lock(&(vubinfmt->mutex)); pathname++; switch(strcase(pathname)) { case 0: if ((flags & O_ACCMODE) != O_RDONLY) { errno = EPERM; retval = -1; } else pseudofile_open(vu_vubinfmt_root_upcall, vubinfmt, flags, fdprivate); break; case (STRCASE(s,t,a,t,u,s)): pseudofile_open(vu_vubinfmt_status_upcall, vubinfmt, flags, fdprivate); break; case (STRCASE(r,e,g,i,s,t,e,r)): if ((flags & O_ACCMODE) != O_WRONLY) { errno = EPERM; retval = -1; } else pseudofile_open(vu_vubinfmt_register_upcall, vubinfmt, flags, fdprivate); break; default: { struct vubinfmt_entry_t *this; this = vubinfmt_search(pathname, vubinfmt->head); if (this) { pseudofile_open(vu_binfmt_entry_upcall, this, flags, fdprivate); } else { errno = ENOENT; retval = -1; } } break; } pthread_mutex_unlock(&(vubinfmt->mutex)); return retval; } int vu_vubinfmt_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vu_service_t *s = vu_mod_getservice(); struct vubinfmt_t *new = malloc(sizeof(struct vubinfmt_t)); if (new == NULL) goto err_nomem_binfmt; pthread_mutex_init(&(new->mutex), NULL); pthread_mutex_lock(&(new->mutex)); switch(strcase(source)) { case STRCASE(n,o,n,e): case STRCASE(slash): new->binfmt_ht = vuht_add(CHECKBINFMT, NULL, 0, s, 0, vubinfmt_confirm, new); break; default: new->binfmt_ht = vuht_add(CHECKBINFMT, source, strlen(source), s, 0, vubinfmt_confirm, new); break; } new->path_ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, new); new->enabled = 1; new->next_ino = 5; new->head = NULL; errno = 0; pthread_mutex_unlock(&(new->mutex)); return 0; err_nomem_binfmt: errno = ENOMEM; return -1; } int vu_vubinfmt_umount2(const char *target, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); int ret_value; if ((ret_value = vuht_del(ht, flags)) < 0) { errno = -ret_value; return -1; } return 0; } void vu_vubinfmt_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vubinfmt_t *vubinfmt = vuht_get_private_data(ht); switch (type) { case CHECKPATH: vubinfmt->path_ht = NULL; break; case CHECKBINFMT: vubinfmt->binfmt_ht = NULL; break; } if (vubinfmt->path_ht == NULL && vubinfmt->binfmt_ht == NULL) { pthread_mutex_destroy(&(vubinfmt->mutex)); free(vubinfmt); } } void *vu_vubinfmt_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, close) = pseudofile_close; vu_syscall_handler(s, read) = pseudofile_read; vu_syscall_handler(s, write) = pseudofile_write; vu_syscall_handler(s, lseek) = pseudofile_lseek; vu_syscall_handler(s, getdents64) = pseudofile_getdents64; #pragma GCC diagnostic pop return NULL; } int vu_vubinfmt_fini(void *private) { return 0; } __attribute__((constructor)) static void init(void) { debug_set_name(B, "VUBINFMT"); } __attribute__((destructor)) static void fini(void) { debug_set_name(B, ""); } vuos-0.9.2/vudev/000077500000000000000000000000001476575172100136665ustar00rootroot00000000000000vuos-0.9.2/vudev/CMakeLists.txt000066400000000000000000000006261476575172100164320ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(VU_MOD_TARGET vudev) file(GLOB_RECURSE VUDEV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUDEV_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} stropt) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vudev/vudev.c000066400000000000000000000362161476575172100151730ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * with contributions by Alessio Volpe * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define VUDEV_VUMODULE_FLAGS (VU_USE_PRW) VU_PROTOTYPES(vudev) struct vu_module_t vu_module = { .name = "vudev", .description = "vu virtual devices", .flags = VUDEV_VUMODULE_FLAGS }; #define VUDEVFLAGS_DEVID 1 struct vudev_t { void *dlhandle; struct vudev_operations_t *devops; pthread_mutex_t mutex; unsigned int flags; struct vu_stat stat; int inuse; void *private_data; struct vuht_entry_t *path_ht; struct vuht_entry_t *dev_ht; }; void *vudev_get_private_data(struct vudev_t *vudev) { if (vudev == NULL) return NULL; else return vudev->private_data; } void vudev_set_devtype(struct vudev_t *vudev, mode_t devtype) { if (S_ISCHR(devtype) || S_ISBLK(devtype)) vudev->stat.st_mode = (vudev->stat.st_mode & ~S_IFMT) | (devtype & S_IFMT); } static int vudev_get_subdev(const char *pathname, struct vuht_entry_t *ht, struct vudev_t *vudev) { if (ht == vudev->dev_ht) { const dev_t *rdev = vuht_get_obj(ht); return minor(*rdev) - minor(vudev->stat.st_rdev); } else return strtoul(pathname, NULL, 0); } int vu_vudev_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct vudevfd_t *vudevfd = malloc(sizeof(struct vudevfd_t)); struct vuht_entry_t *ht = vu_mod_getht(); struct vudev_t *vudev = vu_get_ht_private_data(); int retval; if (vudevfd == NULL) { errno = ENOMEM; return -1; } vudevfd->subdev = vudev_get_subdev(pathname, ht, vudev); vudevfd->offset = 0; vudevfd->flags = flags; vudevfd->fdprivate = NULL; vudevfd->vudev = vudev; /* access control */ pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->open ? vudev->devops->open(pathname, mode, vudevfd) : (errno = ENOSYS, -1); if (retval >= 0) *fdprivate = vudevfd; else free(vudevfd); pthread_mutex_unlock(&(vudev->mutex)); printkdebug(D,"OPEN path:%s flags:%d -> %d %p", pathname, flags, retval, vudevfd); return retval; } int vu_vudev_close(int fd, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; int retval; printkdebug(D,"CLOSE %p", vudevfd); pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->close ? vudev->devops->close(fd, vudevfd) : (errno = ENOSYS, -1); if (retval == 0) free(vudevfd); pthread_mutex_unlock(&(vudev->mutex)); return retval; } #if (VUDEV_VUMODULE_FLAGS & VU_USE_PRW) ssize_t vu_vudev_read(int fd, void *buf, size_t count, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"READ %d %p", fd, vudevfd); if((vudevfd->flags & O_WRONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->read(fd, buf, count, vudevfd); pthread_mutex_unlock(&(vudev->mutex)); return retval; } ssize_t vu_vudev_write(int fd, const void *buf, size_t count, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"WRITE %d %p", fd, vudevfd); if((vudevfd->flags & O_RDONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->write(fd, buf, count, vudevfd); pthread_mutex_unlock(&(vudev->mutex)); return retval; } #else ssize_t vu_vudev_read(int fd, void *buf, size_t count, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"READ %d %p", fd, vudevfd); if((vudevfd->flags & O_WRONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); if(vudev->devops->read) retval = vudev->devops->read(fd, buf, count, vudevfd); else { retval = vudev->devops->pread ? vudev->devops->pread(fd, buf, count, vudevfd->offset, vudevfd) : (errno = ENOSYS, -1); if (retval > 0) vudevfd->offset += retval; } pthread_mutex_unlock(&(vudev->mutex)); return retval; } ssize_t vu_vudev_write(int fd, const void *buf, size_t count, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"WRITE %d %p", fd, vudevfd); if((vudevfd->flags & O_RDONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); if(vudev->devops->write) retval = vudev->devops->write(fd, buf, count, vudevfd); else { retval = vudev->devops->pwrite ? vudev->devops->pwrite(fd, buf, count, vudevfd->offset, vudevfd) : (errno = ENOSYS, -1); if (retval > 0) vudevfd->offset += retval; } pthread_mutex_unlock(&(vudev->mutex)); return retval; } #endif ssize_t vu_vudev_pread64(int fd, void *buf, size_t count, off_t offset, int flags, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"PREAD %d %p", fd, vudevfd); if((vudevfd->flags & O_WRONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->pread ? vudev->devops->pread(fd, buf, count, offset, vudevfd) : (errno = ENOSYS, -1); pthread_mutex_unlock(&(vudev->mutex)); return retval; } ssize_t vu_vudev_pwrite64(int fd, const void *buf, size_t count, off_t offset, int flags, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; ssize_t retval; printkdebug(D,"PWRITE %d %p", fd, vudevfd); if((vudevfd->flags & O_RDONLY) != 0) { errno = EBADF; return -1; } pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->pwrite ? vudev->devops->pwrite(fd, buf, count, offset, vudevfd) : (errno = ENOSYS, -1); pthread_mutex_unlock(&(vudev->mutex)); return retval; } #if 0 int vu_vudev_access(char *path, int mode, int flags) { return 0; } #endif off_t vu_vudev_lseek(int fd, off_t offset, int whence, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vudevfd->vudev; off_t retval; pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->lseek ? vudev->devops->lseek(fd, offset, whence, vudevfd) : (errno = ENOSYS, -1); if (retval != -1) vudevfd->offset = retval; pthread_mutex_unlock(&(vudev->mutex)); printkdebug(D,"LSEEK %d %p retval %lu", fd, vudevfd, retval); return retval; } int vu_vudev_ioctl(int fd, unsigned long request, void *buf, uintptr_t addr, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vu_get_ht_private_data(); int retval; printkdebug(D,"IOCTL %d %p %ld", fd, vudevfd, request); pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->ioctl ? vudev->devops->ioctl(fd, request, buf, vudevfd) : (errno = ENOSYS, -1); pthread_mutex_unlock(&(vudev->mutex)); return retval; } int vu_vudev_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, void *fdprivate) { struct vudevfd_t *vudevfd = fdprivate; struct vudev_t *vudev = vu_get_ht_private_data(); int retval; printkdebug(D,"EPOLL_CTL %d %p %d", fd, vudevfd, op); pthread_mutex_lock(&(vudev->mutex)); retval = vudev->devops->epoll_ctl ? vudev->devops->epoll_ctl(epfd, op, fd, event, vudevfd) : (errno = ENOSYS, -1); pthread_mutex_unlock(&(vudev->mutex)); return retval; } int vu_vudev_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); struct vudev_t *vudev = vu_get_ht_private_data(); printkdebug(D,"LSTAT %s", pathname); memcpy(buf, &vudev->stat, sizeof(struct vu_stat)); buf->st_rdev = makedev(major(buf->st_rdev), minor(buf->st_rdev) + vudev_get_subdev(pathname, ht, vudev)); return 0; } int vu_vudev_lchown(const char *pathname, uid_t owner, gid_t group, int fd, void *fdprivate) { struct vudev_t *vudev = vu_get_ht_private_data(); printkdebug(D,"LCHOWN %s", pathname); /* XXX access control */ pthread_mutex_lock(&(vudev->mutex)); if (owner != (uid_t) -1) vudev->stat.st_uid = owner; if (group != (gid_t) -1) vudev->stat.st_gid = group; vudev->stat.st_ctime = time(NULL); pthread_mutex_unlock(&(vudev->mutex)); return 0; } int vu_vudev_chmod(const char *pathname, mode_t mode, int fd, void *fdprivate) { struct vudev_t *vudev = vu_get_ht_private_data(); printkdebug(D,"LCHMOD %s", pathname); /* XXX access control */ pthread_mutex_lock(&(vudev->mutex)); vudev->stat.st_mode = (vudev->stat.st_mode & S_IFMT) | (mode & (S_IRWXU|S_IRWXG|S_IRWXO)); pthread_mutex_unlock(&(vudev->mutex)); return 0; } static int vudev_confirm_path(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vudev_t *vudev = vuht_get_private_data(ht); char *path = arg; int subdev = strtoul(path, NULL, 10); if (subdev < 0) return 0; else if (vudev->devops->confirm_subdev) return vudev->devops->confirm_subdev(subdev, vudev); else return subdev == 0; } static int vudev_confirm_dev(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vudev_t *vudev = vuht_get_private_data(ht); dev_t *dev = arg; if (major(*dev) != major(vudev->stat.st_rdev)) return 0; else { int subdev = minor(*dev) - minor(vudev->stat.st_rdev); if (subdev < 0) return 0; else if (vudev->devops->confirm_subdev) return vudev->devops->confirm_subdev(subdev, vudev); else return subdev == 0; } } static void set_mount_options(const char *input, struct vudev_t *vudev) { int tagc = stropt(input, NULL, NULL, 0); if(tagc > 1) { char buf[strlen(input)+1]; char *tags[tagc]; char *args[tagc]; stropt(input, tags, args, buf); for (int i=0; tags[i] != NULL; i++) { if (args[i]) { switch(strcase(tags[i])) { case STRCASE(m,o,d,e): vudev->stat.st_mode = (vudev->stat.st_mode & S_IFMT) | (strtoul(args[i], NULL, 8) & 0777); break; case STRCASE(u,i,d): vudev->stat.st_uid = strtoul(args[i], NULL, 0); break; case STRCASE(g,i,d): vudev->stat.st_gid = strtoul(args[i], NULL, 0); break; case STRCASE(m,a,j,o,r): vudev->stat.st_rdev = makedev(strtoul(args[i], NULL, 0), minor(vudev->stat.st_rdev)); break; case STRCASE(m,i,n,o,r): vudev->stat.st_rdev = makedev(major(vudev->stat.st_rdev), strtoul(args[i], NULL, 0)); break; } } else { switch(strcase(tags[i])) { case STRCASE(c,h,r,d,e,v): case STRCASE(c,h,a,r): case STRCASE(c,h,r): vudev->stat.st_mode &= ~S_IFMT; vudev->stat.st_mode |= S_IFCHR; break; case STRCASE(b,l,k,d,e,v): case STRCASE(b,l,k): vudev->stat.st_mode &= ~S_IFMT; vudev->stat.st_mode |= S_IFBLK; break; case STRCASE(d,e,v,i,d): vudev->flags |= VUDEVFLAGS_DEVID; break; } } } } } int vu_vudev_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vudev_operations_t *devops = NULL; void *dlhandle = vu_mod_dlopen(filesystemtype, RTLD_NOW); if (data == NULL) data = ""; printkdebug(D,"MOUNT source:%s target:%s type:%s flags:0x%x data:%s", source, target, filesystemtype, mountflags, data); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" if(dlhandle == NULL || (devops = dlsym(dlhandle,"vudev_ops")) == NULL) { #pragma GCC diagnostic pop if (dlhandle != NULL) { printk(KERN_ERR "%s",dlerror()); dlclose(dlhandle); } errno = ENOSYS; return -1; } else { struct vu_service_t *s = vu_mod_getservice(); struct vudev_t *new = malloc(sizeof(struct vudev_t)); struct vu_stat tstat; if (new == NULL) goto err_nomem_dev; new->dlhandle = dlhandle; memset(&new->stat, 0, sizeof(struct vu_stat)); new->stat.st_blksize = getpagesize(); new->stat.st_mode = S_IFCHR | 0600; new->stat.st_uid = getuid(); new->stat.st_gid = getgid(); new->stat.st_atime = new->stat.st_ctime = new->stat.st_mtime = time(NULL); if (vu_stat(target, &tstat) == 0) { new->stat.st_rdev = tstat.st_rdev; if (S_ISCHR(tstat.st_mode) | S_ISBLK (tstat.st_mode)) new->stat.st_mode = (tstat.st_mode & S_IFMT) | 0600; } new->flags = 0; new->devops = devops; new->private_data = NULL; set_mount_options(data, new); pthread_mutex_init(&(new->mutex), NULL); pthread_mutex_lock(&(new->mutex)); if (devops->init) { new->private_data = devops->init(source, mountflags, data, new); if (new->private_data == NULL) goto err_init_null; } new->path_ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, VUFLAG_TRAILINGNUMBERS, vudev_confirm_path, new); if (new->flags & VUDEVFLAGS_DEVID) { if(S_ISCHR(new->stat.st_mode)) new->dev_ht = vuht_add(CHECKCHRDEVICE, NULL, 0, s, 0, vudev_confirm_dev, new); else if(S_ISBLK(new->stat.st_mode)) new->dev_ht = vuht_add(CHECKBLKDEVICE, NULL, 0, s, 0, vudev_confirm_dev, new); } else new->dev_ht = NULL; pthread_mutex_unlock(&(new->mutex)); return 0; err_init_null: pthread_mutex_unlock(&(new->mutex)); free(new); dlclose(dlhandle); if (errno == 0) errno = EINVAL; return -1; err_nomem_dev: dlclose(dlhandle); errno = ENOMEM; return -1; } } int vu_vudev_umount2(const char *target, int flags) { struct vudev_t *vudev = vu_get_ht_private_data(); if (vudev == NULL) { errno = EINVAL; return -1; } else { pthread_mutex_lock(&(vudev->mutex)); if (vudev->inuse && !(flags & MNT_DETACH)) { pthread_mutex_unlock(&(vudev->mutex)); errno = EBUSY; return -1; } else { /*cleanup and umount_internal will do the right umounting sequence in a lazy way*/ if (vudev->path_ht != NULL) vuht_del(vudev->path_ht, flags); if (vudev->dev_ht != NULL) vuht_del(vudev->dev_ht, flags); pthread_mutex_unlock(&(vudev->mutex)); printkdebug(D,"UMOUNT target:%s flags:%d %p", target, flags, vudev); return 0; } } } void vu_vudev_cleanup(uint8_t type, void *arg, int arglen,struct vuht_entry_t *ht) { struct vudev_t *vudev = vuht_get_private_data(ht); switch (type) { case CHECKPATH: vudev->path_ht = NULL; break; case CHECKCHRDEVICE: case CHECKBLKDEVICE: vudev->dev_ht = NULL; break; } if(vudev->path_ht == NULL && vudev->dev_ht == NULL) { printkdebug(D,"CLEANUP %p", vudev); if(vudev->devops->fini) vudev->devops->fini(vudev->private_data); pthread_mutex_destroy(&(vudev->mutex)); dlclose(vudev->dlhandle); free(vudev); } } __attribute__((constructor)) static void init(void) { debug_set_name(D, "VUDEV"); } __attribute__((destructor)) static void fini(void) { debug_set_name(D, ""); } vuos-0.9.2/vudev_modules/000077500000000000000000000000001476575172100154165ustar00rootroot00000000000000vuos-0.9.2/vudev_modules/CMakeLists.txt000066400000000000000000000011331476575172100201540ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${VU_DYN_HEADER_PATH}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VUDEV_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) foreach(VUDEV_MOD ${VUDEV_MODULES}) string(REGEX REPLACE "\.c$" "" VUDEV_MOD_FILE ${VUDEV_MOD}) get_filename_component(VUDEV_MOD_TARGET ${VUDEV_MOD_FILE} NAME) add_library(${VUDEV_MOD_TARGET} SHARED ${VUDEV_MOD}) set_target_properties(${VUDEV_MOD_TARGET} PROPERTIES PREFIX "") install(TARGETS ${VUDEV_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) endforeach(VUDEV_MOD) vuos-0.9.2/vudev_modules/vudevnull.c000066400000000000000000000031541476575172100176110ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * with contributions by Alessio Volpe * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include static int null_open(const char *pathname, mode_t mode, struct vudevfd_t *vudevfd) { printkdebug(D,"null_open [%s]", pathname); return 0; } static int null_close(int fd, struct vudevfd_t *vudevfd) { printkdebug(D,"null_close", NULL); return 0; } static ssize_t null_read (int fd, void *buf, size_t count, struct vudevfd_t *vudevfd) { printkdebug(D,"null_read: [%d]", count); return 0; } static ssize_t null_write(int fd, const void *buf, size_t count, struct vudevfd_t *vudevfd) { printkdebug(D,"null_write: [%d]", count); return count; } struct vudev_operations_t vudev_ops = { .open = null_open, .close = null_close, .read = null_read, .write= null_write, }; vuos-0.9.2/vudev_modules/vudevpartx.c000066400000000000000000000342151476575172100177770ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * Alessio Volpe * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IDE_BLOCKSIZE 512 #define IDE_BLOCKSIZE_LOG 9 #define IDE_HEADER_OFFSET 446 #define MBR_GPT_PARTITION_TYPE 0xEE /* Intel EFI GUID Partition Table */ #define GPT_HEADER_SIGNATURE 0x5452415020494645ULL /* EFI PART */ #define GPT_GUID_SIZE 16 #define GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY 0x1000000000000000ULL #define PART_ADDRBASE(partition) (((off_t) partition->LBAbegin) << IDE_BLOCKSIZE_LOG) #define PART_ADDRMAX(partition) (((off_t) partition->LBAnoblocks) << IDE_BLOCKSIZE_LOG) struct vupartition_t { uint8_t bootflag; uint8_t type; uint8_t readonly; uint64_t LBAbegin; uint64_t LBAnoblocks; }; struct vumbr_t { int fd; off_t size; int part_table_last_elem; struct vupartition_t *part_table; }; /******************************************************************************/ /************************************UTILS*************************************/ struct mbr_header_t { uint8_t code[IDE_HEADER_OFFSET]; struct { uint8_t bootflag; uint8_t chs_begin[3]; uint8_t type; uint8_t chs_end[3]; uint32_t lba_begin; uint32_t lba_noblocks; } vumbrpart[4] __attribute__((__packed__)); uint8_t signature[2]; }; struct gpt_header_t { uint64_t signature; /* Signature EFI PART (little-endian) */ uint32_t revision; uint32_t header_size; /* Header size in bytes (little-endian) */ uint32_t header_crc32; /* Header CRC checksum */ uint32_t reserved1; /* Must be 0 */ uint64_t current_lba; /* Current LBA (location of this header copy) */ uint64_t backup_lba; /* Backup LBA (location of the other header copy) */ uint64_t first_usable_lba; /* First usable LBA for partitions (primary partition table last LBA + 1) */ uint64_t last_usable_lba; /* Last usable LBA (secondary partition table first LBA - 1) */ uint8_t disk_guid[GPT_GUID_SIZE]; /* Disk GUID */ uint64_t starting_lba; /* Starting LBA of array of partition entries (always 2 in primary copy) */ uint32_t numberof_partiton_entries; /* Number of partition entries in array */ uint32_t sizeof_partition_entry; /* Size of a single partition entry (usually 128) */ uint32_t partition_entry_array_crc32; /* Partition CRC checksum */ uint8_t reserved2[512 - 92]; /* Must all be 0 */ }; struct gpt_entry_t { uint8_t type[GPT_GUID_SIZE]; /* Partition type GUID */ uint8_t guid[GPT_GUID_SIZE]; /* Unique partition GUID */ uint64_t lba_start; /* First LBA (little endian) */ uint64_t lba_end; /* Last LBA */ uint64_t attrs; /* Attribute flags */ uint8_t name[72]; /* Partition name */ }; #define BLOCKPART (IDE_BLOCKSIZE / sizeof(struct gpt_entry_t)) static const uint8_t unused_entry[GPT_GUID_SIZE] = {0}; static int _read_gpt(int fd, off_t size, struct vupartition_t *part_table, int maxpart) { struct gpt_header_t gpt_header; if (pread64(fd, &gpt_header, sizeof(gpt_header), IDE_BLOCKSIZE) <= 0) { printk(KERN_ERR "Cannot read disk header"); return 0; } if (le64toh(gpt_header.signature) != GPT_HEADER_SIGNATURE) { if (part_table) /* avoid double warning */ printk(KERN_ERR "Bad GPT signature 0x%llx\n", le64toh(gpt_header.signature)); return 0; } else { int part_table_last_elem = 0; uint64_t starting_lba = le64toh(gpt_header.starting_lba); uint32_t numberof_partiton_entries = le32toh(gpt_header.numberof_partiton_entries); uint32_t blk; uint32_t nblks = (numberof_partiton_entries + BLOCKPART - 1) / BLOCKPART; struct gpt_entry_t gpt_entry_buf[BLOCKPART]; for (blk = 0; blk < nblks ; blk++) { uint32_t i; if (pread64(fd, gpt_entry_buf, sizeof(gpt_entry_buf), (starting_lba + blk) * IDE_BLOCKSIZE) <= 0) { printk(KERN_ERR "Cannot read block @ %lld\n", (starting_lba + blk) * IDE_BLOCKSIZE); } else { for (i = 0; i < BLOCKPART && blk * BLOCKPART + i < numberof_partiton_entries; i++) { int index = blk * BLOCKPART + i + 1; if (memcmp(&gpt_entry_buf[i].type, unused_entry, GPT_GUID_SIZE) != 0) { part_table_last_elem = index; if (part_table && index <= maxpart) { struct vupartition_t *new = &part_table[index]; new->bootflag = 0; new->type = MBR_GPT_PARTITION_TYPE; new->readonly = (le64toh(gpt_entry_buf[i].attrs) & GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY) != 0; new->LBAbegin = le64toh(gpt_entry_buf[i].lba_start) ; new->LBAnoblocks = le64toh(gpt_entry_buf[i].lba_end) - new->LBAbegin + 1; } } } } } return part_table_last_elem; } } static int _read_mbr(int fd, off_t size, struct vupartition_t *part_table, int maxpart) { uint8_t vumbr_signature[2] = {0x55, 0xAA}; uint32_t ext_part_base = 0; struct mbr_header_t vumbr_header; if (pread64(fd, &vumbr_header, sizeof(vumbr_header), (off_t) 0) <= 0) { printk(KERN_ERR "Cannot read disk header"); return 0; } if (part_table) { part_table[0].LBAnoblocks = (size >> IDE_BLOCKSIZE_LOG); part_table[0].type = 0xff; part_table[0].readonly = 0; part_table[0].bootflag = 0; } if(memcmp(vumbr_header.signature, vumbr_signature, 2) != 0) { if (part_table) /* avoid double warning */ printk(KERN_ERR "Bad MBR signature %x %x\n", vumbr_header.signature[0], vumbr_header.signature[1]); return 0; } else if (vumbr_header.vumbrpart[0].type == MBR_GPT_PARTITION_TYPE) { return _read_gpt(fd, size, part_table, maxpart); } else { /* MBR is okay. Read MBR */ int i, part_table_last_elem = 4; unsigned int offset = 0; for (i = 0; i < 4; i++) { if (part_table && part_table_last_elem <= maxpart) { struct vupartition_t *new = &part_table[i+1]; new->bootflag = vumbr_header.vumbrpart[i].bootflag; new->type = vumbr_header.vumbrpart[i].type; new->readonly = 0; new->LBAbegin = le32toh(vumbr_header.vumbrpart[i].lba_begin); new->LBAnoblocks = le32toh(vumbr_header.vumbrpart[i].lba_noblocks); } if(vumbr_header.vumbrpart[i].type == 5) {/* extended partition*/ if (ext_part_base == 0) ext_part_base = le32toh(vumbr_header.vumbrpart[i].lba_begin); else printk(KERN_ERR "There are more than one extended partitions against the specifications\n", vumbr_header.vumbrpart[i].type); } } /* Read the chain of logical partitions inside the extended partition */ while (ext_part_base > 0) { off_t base = ((off_t)(ext_part_base + offset)) << IDE_BLOCKSIZE_LOG; if (pread64(fd, &vumbr_header, sizeof(vumbr_header), base) <= 0) { printk(KERN_ERR "Cannot read block %lld\n", base); ext_part_base = 0; } else if(memcmp(vumbr_header.signature, vumbr_signature, 2) != 0) { printk(KERN_ERR "Bad signature in block %lld=%x %x\n", base, vumbr_header.signature[0],vumbr_header.signature[1]); ext_part_base = 0; } else { if(vumbr_header.vumbrpart[0].type != 0) { ++part_table_last_elem; if (part_table && part_table_last_elem <= maxpart) { struct vupartition_t *new = &part_table[part_table_last_elem]; new->bootflag = vumbr_header.vumbrpart[0].bootflag; new->type = vumbr_header.vumbrpart[0].type; new->readonly = 0; new->LBAbegin = le32toh(vumbr_header.vumbrpart[0].lba_begin) + ext_part_base + offset; new->LBAnoblocks = le32toh(vumbr_header.vumbrpart[0].lba_noblocks); } if(vumbr_header.vumbrpart[1].type == 5) offset = le32toh(vumbr_header.vumbrpart[1].lba_begin); else ext_part_base=0; } } } return part_table_last_elem; } } static size_t _ck_size(struct vupartition_t *partition, size_t count, off_t offset) { off_t partsize = PART_ADDRMAX(partition); if (offset > partsize) { errno = ENXIO; return -1; } else if (offset + (off_t) count > partsize) return partsize - offset; else return count; } /******************************************************************************/ /***********************************SYSCALL************************************/ int vumbr_confirm_subdev(int subdev, struct vudev_t *vudev) { struct vumbr_t *vumbr = vudev_get_private_data(vudev); return subdev >= 0 && subdev <= vumbr->part_table_last_elem && vumbr->part_table[subdev].type != 0; } int vumbr_open(const char *pathname, mode_t mode, struct vudevfd_t *vdevfd) { struct vumbr_t *vumbr = vudev_get_private_data(vdevfd->vudev); struct vupartition_t *partition; int subdev; subdev = vdevfd->subdev; if (vumbr_confirm_subdev(subdev, vdevfd->vudev)) { partition = malloc(sizeof(struct vupartition_t)); if (partition == NULL) { errno = ENOMEM; return -1; } else *partition = vumbr->part_table[subdev]; } else { errno = EINVAL; return -1; } vdevfd->fdprivate = partition; return 0; } int vumbr_close(int fd, struct vudevfd_t *vdevfd) { struct vupartition_t *partition = vdevfd->fdprivate; if (partition) free(partition); return 0; } ssize_t vumbr_pread64(int fd, void *buf, size_t count, off_t offset, struct vudevfd_t *vdevfd) { struct vumbr_t *vumbr = vudev_get_private_data(vdevfd->vudev); struct vupartition_t *partition = vdevfd->fdprivate; if ((count = _ck_size(partition, count, offset)) <= 0) return count; else return pread64(vumbr->fd, buf, count, offset + PART_ADDRBASE(partition)); } ssize_t vumbr_pwrite64(int fd, const void *buf, size_t count, off_t offset, struct vudevfd_t *vdevfd) { struct vumbr_t *vumbr = vudev_get_private_data(vdevfd->vudev); struct vupartition_t *partition = vdevfd->fdprivate; if ((count = _ck_size(partition, count, offset)) <= 0) return count; else return pwrite64(vumbr->fd, buf, count, offset + PART_ADDRBASE(partition)); } off_t vumbr_lseek(int fd, off_t offset, int whence, struct vudevfd_t *vdevfd) { struct vupartition_t *partition = vdevfd->fdprivate; off_t ret_value; switch (whence) { case SEEK_SET: ret_value = offset; break; case SEEK_CUR: ret_value = vdevfd->offset + offset; break; case SEEK_END: ret_value = PART_ADDRMAX(partition) + offset; break; default: errno = EINVAL; ret_value = (off_t) -1; break; } return ret_value; } int vumbr_ioctl(int fd, unsigned long request, void *addr, struct vudevfd_t *vdevfd){ if (fd >= 0) { struct vumbr_t *vumbr = vudev_get_private_data(vdevfd->vudev); struct vupartition_t *partition = vdevfd->fdprivate; switch (request) { case BLKROGET: { *(int *)addr = partition->readonly; break; } case BLKROSET:{ partition->readonly = (*(int *)addr > 0)? 1:0; break; } case BLKSSZGET: *(int *)addr = IDE_BLOCKSIZE; break; case BLKGETSIZE: *(uint32_t *)addr = partition->LBAnoblocks; break; case BLKGETSIZE64: *(uint64_t *)addr = (partition->LBAnoblocks) << IDE_BLOCKSIZE_LOG; break; case BLKRRPART: { int newpart_table_last_elem = _read_mbr(vumbr->fd, vumbr->size, NULL, 0); struct vupartition_t *newpart = calloc(newpart_table_last_elem + 1, sizeof(struct vupartition_t)); if (newpart == NULL) errno = ENOMEM; else { free(vumbr->part_table); vumbr->part_table = newpart; vumbr->part_table_last_elem = _read_mbr(vumbr->fd, vumbr->size, newpart, newpart_table_last_elem); } break; } case HDIO_GETGEO: { if (ioctl(vumbr->fd, HDIO_GETGEO, addr) < 0) { struct hd_geometry *geometry = addr; geometry->heads = 255; geometry->sectors = 63; geometry->cylinders = (vumbr->size >> IDE_BLOCKSIZE_LOG) / (geometry->heads * geometry->sectors); if (geometry->cylinders * geometry->heads * geometry->sectors < (vumbr->size >> IDE_BLOCKSIZE_LOG)) geometry->cylinders += 1; geometry->start = PART_ADDRBASE(partition); } break; } default: errno = EINVAL; return -1; } return 0; } else return -1; } void *vumbr_init(const char *source, unsigned long flags, const char *args, struct vudev_t *vudev) { struct vumbr_t *vumbr; int open_flags = (flags & MS_RDONLY) ? O_RDONLY|O_CLOEXEC : O_RDWR|O_CLOEXEC; if((vumbr = calloc(1, sizeof(struct vumbr_t))) == NULL) { errno = ENOMEM; return NULL; } if((vumbr->fd = open(source, open_flags)) == -1) { free(vumbr); return NULL; } if((vumbr->size = lseek(vumbr->fd, 0, SEEK_END)) == -1) { if(ioctl(vumbr->fd, BLKGETSIZE64, &(vumbr->size)) < 0) { close(vumbr->fd); free(vumbr); return NULL; } } vumbr->part_table_last_elem = _read_mbr(vumbr->fd, vumbr->size, NULL, 0); vumbr->part_table = calloc(vumbr->part_table_last_elem + 1, sizeof(struct vupartition_t)); if (vumbr->part_table == NULL) { close(vumbr->fd); free(vumbr); errno = ENOMEM; return NULL; } vumbr->part_table_last_elem = _read_mbr(vumbr->fd, vumbr->size, vumbr->part_table, vumbr->part_table_last_elem); vudev_set_devtype(vudev, S_IFBLK); return vumbr; } int vumbr_fini(void *private_data) { struct vumbr_t *vumbr = private_data; if(vumbr) { if (vumbr->part_table) free(vumbr->part_table); close(vumbr->fd); free(vumbr); private_data = NULL; } return 0; } struct vudev_operations_t vudev_ops = { .confirm_subdev = vumbr_confirm_subdev, .open = vumbr_open, .close = vumbr_close, .pread = vumbr_pread64, .pwrite = vumbr_pwrite64, .lseek = vumbr_lseek, .ioctl = vumbr_ioctl, .init = vumbr_init, .fini = vumbr_fini, }; vuos-0.9.2/vudev_modules/vudevramdisk.c000066400000000000000000000146451476575172100203000ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * with contributions by Alessio Volpe * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #define STD_SIZE (64*1024) #define STD_SECTORSIZE 512 #define READONLY 1 #define MBR 2 #define RAMDISK_SIZE(ramdisk) (ramdisk->rd_size * STD_SECTORSIZE) #define GET_CYLINDERS(ramdisk) \ ((ramdisk->rd_size + (ramdisk->geometry.heads*ramdisk->geometry.sectors) -1) / (ramdisk->geometry.heads*ramdisk->geometry.sectors)) struct vuramdisk_t { char flags; char *diskdata; size_t rd_size; struct hd_geometry geometry; }; /******************************************************************************/ /************************************UTILS*************************************/ static inline ssize_t _get_size(char unit, ssize_t size) { switch (unit) { case 'k': case 'K': size *= 1024 / STD_SECTORSIZE; return size; case 'm': case 'M': size *= 1024 * 1024 / STD_SECTORSIZE; return size; case 'g': case 'G': size *= 1024 * 1024 * 1024 / STD_SECTORSIZE; return size; default: return size; } } static inline ssize_t _get_strsize(char *size) { return _get_size(size[strlen(size) - 1], strtoull(size, NULL, 0)); } static void set_mount_options(const char *input, struct vuramdisk_t *ramdisk) { int tagc = stropt(input, NULL, NULL, 0); if(tagc > 1) { char buf[strlen(input)+1]; char *tags[tagc]; char *args[tagc]; stropt(input, tags, args, buf); for (int i=0; tags[i] != NULL; i++) { switch(strcase(tags[i])) { case STRCASE(s,i,z,e): if (args[i]) ramdisk->rd_size = _get_strsize(args[i]); break; case STRCASE(m,b,r): ramdisk->flags = MBR; break; } } } } static inline ssize_t _ck_size(struct vuramdisk_t *ramdisk, size_t count, off_t offset) { if((size_t) offset >= RAMDISK_SIZE(ramdisk)) return 0; count = (offset + count <= RAMDISK_SIZE(ramdisk))? count: (RAMDISK_SIZE(ramdisk) - offset); return count; } /******************************************************************************/ /***********************************SYSCALL************************************/ int vuramdisk_open(const char *pathname, mode_t mode, struct vudevfd_t *vudevfd) { return 0; } int vuramdisk_close(int fd, struct vudevfd_t *vudevfd) { return 0; } ssize_t vuramdisk_pread(int fd, void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd) { struct vuramdisk_t *ramdisk = vudev_get_private_data(vudevfd->vudev); count = _ck_size(ramdisk, count, offset); memcpy(buf, (ramdisk->diskdata + offset), count); return count; } ssize_t vuramdisk_pwrite(int fd, const void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd) { struct vuramdisk_t *ramdisk = vudev_get_private_data(vudevfd->vudev); if(ramdisk->flags & READONLY) { errno = EBADF; return -1; } count = _ck_size(ramdisk, count, offset); memcpy((ramdisk->diskdata + offset), buf, count); return count; } off_t vuramdisk_lseek(int fd, off_t offset, int whence, struct vudevfd_t *vudevfd) { struct vuramdisk_t *ramdisk = vudev_get_private_data(vudevfd->vudev); off_t ret_value; switch (whence) { case SEEK_SET: ret_value = offset; break; case SEEK_CUR: ret_value = vudevfd->offset + offset; break; case SEEK_END: ret_value = RAMDISK_SIZE(ramdisk) + offset; break; default: errno = EINVAL; ret_value = (off_t) -1; break; } return ret_value; } int vuramdisk_ioctl(int fd, unsigned long request, void *addr, struct vudevfd_t *vudevfd){ if (fd >= 0) { struct vuramdisk_t *ramdisk = vudev_get_private_data(vudevfd->vudev); switch (request) { case BLKROGET: *(int *)addr = (ramdisk->flags & READONLY); break; case BLKROSET: ramdisk->flags |= (*(int *)addr > 0)? READONLY:0; break; case BLKSSZGET: *(int *)addr = STD_SECTORSIZE; break; case BLKGETSIZE: *(int *)addr = ramdisk->rd_size * ((ramdisk->flags & MBR)? 1:STD_SECTORSIZE); break; case BLKGETSIZE64: *(long long *)addr = ramdisk->rd_size * STD_SECTORSIZE; break; case BLKRRPART: break; case HDIO_GETGEO: memcpy(addr, &(ramdisk->geometry), sizeof(struct hd_geometry)); break; default: errno = EINVAL; return -1; } return 0; } else { return -1; } } void *vuramdisk_init(const char *source, unsigned long flags, const char *args, struct vudev_t *vudev) { struct vuramdisk_t *ramdisk; if((ramdisk = calloc(1, sizeof(struct vuramdisk_t))) == NULL) { errno = ENOMEM; return NULL; } set_mount_options(args, ramdisk); if(ramdisk->rd_size == 0) ramdisk->rd_size = STD_SIZE; ramdisk->geometry.start = 0; if (ramdisk->rd_size == (unsigned int) ramdisk->rd_size) { /* 32 */ ramdisk->geometry.heads = 16; ramdisk->geometry.sectors = 16; } else { /* 64 */ ramdisk->geometry.heads = 128; ramdisk->geometry.sectors = 128; } ramdisk->geometry.cylinders = GET_CYLINDERS(ramdisk); ramdisk->rd_size = ramdisk->geometry.heads * ramdisk->geometry.sectors * ramdisk->geometry.cylinders; if((ramdisk->diskdata = calloc(1, ramdisk->rd_size * STD_SECTORSIZE)) == NULL) { free(ramdisk); errno = ENOMEM; return NULL; } vudev_set_devtype(vudev, S_IFBLK); return ramdisk; } int vuramdisk_fini(void *private_data) { struct vuramdisk_t *ramdisk = private_data; if (ramdisk) { free(ramdisk->diskdata); free(ramdisk); private_data = NULL; } return 0; } struct vudev_operations_t vudev_ops = { .open = vuramdisk_open, .close = vuramdisk_close, .pread = vuramdisk_pread, .pwrite = vuramdisk_pwrite, .lseek = vuramdisk_lseek, .ioctl = vuramdisk_ioctl, .init = vuramdisk_init, .fini = vuramdisk_fini, }; vuos-0.9.2/vudev_modules/vudevvdi.c000066400000000000000000000264141476575172100174250ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * with contributions by Alessio Volpe * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STD_SECTORSIZE 512 #define STD_SECTORSIZE_OFFSET_MASK 0x1ff #define IS_ALIGNED(offset, count) \ ((offset&STD_SECTORSIZE_OFFSET_MASK) == 0 && (count&STD_SECTORSIZE_OFFSET_MASK) == 0) #define RT_BIT(bit) (1UL << (bit)) #define VD_OPEN_FLAGS_NORMAL 0 #define VD_OPEN_FLAGS_READONLY RT_BIT(0) static void *vboxdd_hdl; static int vboxdd_count; static int (*vboxdd_create) (); static int (*vboxdd_open) (); static int (*vboxdd_close) (); static int (*vboxdd_read) (); static int (*vboxdd_write) (); static int (*vboxdd_flush) (); static uint64_t (*vboxdd_get_size) (); static int (*vboxdd_set_open_flags) (); static int (*vboxdd_get_LCHS_geometry) (); struct vuvdi_t { int fd; void *disk; size_t size; unsigned long flags; }; struct vuvdi_pdmmediageom_t { uint32_t cHeads; uint32_t cSectors; uint32_t cCylinders; }; /******************************************************************************/ /************************************UTILS*************************************/ static int vboxdd_detect_type(int fd, char **disktype) { char buf[8]; if (read(fd, buf, sizeof(buf)) <= 0) { printk(KERN_ERR "cannot read disk type\n"); errno = ENODEV; return -1; } if (strncmp (buf, "conectix", 8) == 0) *disktype = "VHD"; else if (strncmp (buf, "VMDK", 4) == 0) *disktype = "VMDK"; else if (strncmp (buf, "KDMV", 4) == 0) *disktype = "VMDK"; else if (strncmp (buf, "<<<", 3) == 0) *disktype = "VDI"; else { printk(KERN_ERR "cannot autodetect disk type\n"); errno = ENODEV; return -1; } printkdebug(D, "disktype [%s]", *disktype); return 0; } static int dlload_vboxdd(void) { if(vboxdd_count) return ++vboxdd_count; if((vboxdd_hdl = vu_mod_dlopen("/usr/lib/virtualbox/VBoxDD.so", RTLD_NOW)) == NULL) { printk(KERN_ERR "VBoxDD library: not found"); return -1; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vboxdd_create = (int *) dlsym(vboxdd_hdl, "VDCreate"); vboxdd_open = (int *) dlsym(vboxdd_hdl, "VDOpen"); vboxdd_close = (int *) dlsym(vboxdd_hdl, "VDClose"); vboxdd_read = (int *) dlsym(vboxdd_hdl, "VDRead"); vboxdd_write = (int *) dlsym(vboxdd_hdl, "VDWrite"); vboxdd_flush = (int *) dlsym(vboxdd_hdl, "VDFlush"); vboxdd_get_size = (uint64_t *) dlsym(vboxdd_hdl, "VDGetSize"); vboxdd_set_open_flags = (int *) dlsym(vboxdd_hdl, "VDSetOpenFlags"); vboxdd_get_LCHS_geometry = (int *) dlsym(vboxdd_hdl, "VDGetLCHSGeometry"); #pragma GCC diagnostic pop printkdebug(D, "%p %p %p %p %p %p %p %p", vboxdd_create, vboxdd_open, vboxdd_close, vboxdd_read, vboxdd_write, vboxdd_flush, vboxdd_get_size, vboxdd_get_LCHS_geometry); return ++vboxdd_count; } static void dlunload_vboxdd(void) { vboxdd_count--; if (vboxdd_count == 0) { dlclose(vboxdd_hdl); vboxdd_hdl=NULL; } } static inline ssize_t _vuvdi_alined_pread64(void *disk, void *buf, size_t count, off_t offset) { if(count == 0) return 0; if (IS_ALIGNED(offset, count)) { if(vboxdd_read(disk, offset, buf, count) < 0) goto read_err_exit; return count; } char tmp_buf[STD_SECTORSIZE]; size_t over_off, tmp_count, until_count = count; /* Read to align the offset to the next block. */ if((over_off = (offset & STD_SECTORSIZE_OFFSET_MASK))) { if(vboxdd_read(disk, (offset - over_off), tmp_buf, STD_SECTORSIZE) < 0) goto read_err_exit; tmp_count = STD_SECTORSIZE - over_off; if(count < tmp_count) tmp_count = count; memcpy(buf, (tmp_buf + over_off), tmp_count); buf = ((char *) buf) + tmp_count; offset += tmp_count; until_count -= tmp_count; } /* Read all blocks contained in count. */ if((tmp_count = (until_count & ~STD_SECTORSIZE_OFFSET_MASK))) { if(vboxdd_read(disk, offset, buf, tmp_count) < 0) goto read_err_exit; buf = ((char *) buf) + tmp_count; offset += tmp_count; until_count -= tmp_count; } if(until_count > 0) { /* until_count < blocksize */ if(vboxdd_read(disk, offset, tmp_buf, STD_SECTORSIZE) < 0) goto read_err_exit; memcpy(buf, tmp_buf, until_count); } return count; read_err_exit: errno = EIO; return -1; } static inline ssize_t _vuvdi_alined_pwrite64(void *disk, const void *buf, size_t count, off_t offset) { if(count == 0) return 0; if(IS_ALIGNED(offset, count)) { if(vboxdd_write(disk, offset, buf, count) < 0) goto write_err_exit; return count; } char tmp_buf[STD_SECTORSIZE]; size_t over_off, tmp_count, until_count = count; /* Write to align the offset to the next block. */ if((over_off = (offset & STD_SECTORSIZE_OFFSET_MASK))) { if(vboxdd_read(disk, (offset - over_off), tmp_buf, STD_SECTORSIZE) < 0) goto write_err_exit; tmp_count = STD_SECTORSIZE - over_off; if(count < tmp_count) tmp_count = count; memcpy((tmp_buf + over_off), buf, tmp_count); /* Write difference. */ if(vboxdd_write(disk, (offset - over_off), tmp_buf, STD_SECTORSIZE) < 0) goto write_err_exit; buf = ((char *) buf) + (tmp_count); offset += tmp_count; until_count -= tmp_count; } /* Write all blocks contained in count. */ if((tmp_count = (until_count & ~STD_SECTORSIZE_OFFSET_MASK))) { if(vboxdd_write(disk, offset, buf, tmp_count) < 0) goto write_err_exit; buf = ((char *) buf) + tmp_count; offset += tmp_count; until_count -= tmp_count; } if(until_count > 0) { /* until_count < blocksize */ if(vboxdd_read(disk, offset, tmp_buf, STD_SECTORSIZE) < 0) goto write_err_exit; memcpy(tmp_buf, buf, until_count); /* Write difference. */ if(vboxdd_write(disk, offset, tmp_buf, STD_SECTORSIZE) < 0) goto write_err_exit; } return count; write_err_exit: errno = EIO; return -1; } static inline size_t _wrap_count(size_t size, off_t *offset, size_t count) { if(*offset > (off_t) size) *offset = size; if((*offset + count) > size) count = size - *offset; return count; } /******************************************************************************/ /***********************************SYSCALL************************************/ int vuvdi_open(const char *pathname, mode_t mode, struct vudevfd_t *vudevfd) { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); return vdi->fd; } int vuvdi_close(int fd, struct vudevfd_t *vudevfd) { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); vboxdd_flush(vdi->disk); return 0; } ssize_t vuvdi_pread(int fd, void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd) { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); //vudev_printd("offset [%lu] count [%d]", offset, count); count = _wrap_count(vdi->size, &offset, count); return _vuvdi_alined_pread64(vdi->disk, buf, count, offset); } ssize_t vuvdi_pwrite(int fd, const void *buf, size_t count, off_t offset, struct vudevfd_t *vudevfd) { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); //vudev_printd("offset [%lu] count [%d]", offset, count); count = _wrap_count(vdi->size, &offset, count); return _vuvdi_alined_pwrite64(vdi->disk, buf, count, offset); } off_t vuvdi_lseek(int fd, off_t offset, int whence, struct vudevfd_t *vudevfd) { off_t ret_value; switch (whence) { case SEEK_SET: ret_value = offset; break; case SEEK_CUR: ret_value = (vudevfd->offset + offset); break; case SEEK_END: { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); ret_value = vdi->size + offset; break; } default: errno = EINVAL; ret_value = (off_t) -1; break; } return ret_value; } int vuvdi_ioctl(int fd, unsigned long request, void *addr, struct vudevfd_t *vudevfd){ if (fd >= 0) { struct vuvdi_t *vdi = vudev_get_private_data(vudevfd->vudev); switch (request) { case BLKROGET: *(int *)addr = (vdi->flags & MS_RDONLY); break; case BLKROSET: { vdi->flags |= (*(int *)addr > 0)? MS_RDONLY:0; if(vboxdd_set_open_flags(vdi->disk, 0, (vdi->flags&MS_RDONLY)? VD_OPEN_FLAGS_READONLY:VD_OPEN_FLAGS_NORMAL) < 0) goto ioctl_err_exit; break; } case BLKSSZGET: *(int *)addr = STD_SECTORSIZE; break; case BLKGETSIZE: *(int *)addr = vdi->size / STD_SECTORSIZE; break; case BLKGETSIZE64: *(long long *)addr = vdi->size; break; case BLKRRPART: break; case HDIO_GETGEO: { struct { uint32_t cCylinders; uint32_t cHeads; uint32_t cSectors; } vdgeom; if(vboxdd_get_LCHS_geometry(vdi->disk, 0, &vdgeom) < 0) goto ioctl_err_exit; struct hd_geometry *hdg = addr; hdg->heads = (unsigned char) vdgeom.cHeads; hdg->sectors = (unsigned char) vdgeom.cSectors; hdg->cylinders = (unsigned short) vdgeom.cCylinders; hdg->start = 0; break; } default: ioctl_err_exit: errno = EINVAL; return -1; } return 0; } else { return -1; } } static void *vuvdi_init(const char *source, unsigned long flags, const char *args, struct vudev_t *vudev) { struct vuvdi_t *vdi; char *disk_type = "auto"; if (dlload_vboxdd() == -1) return NULL; if((vdi = calloc(1, sizeof(struct vuvdi_t))) == NULL) goto exit_err; if((vdi->fd = open(source, O_RDONLY)) == -1) goto exit_err; vdi->flags = flags; if(vboxdd_create(NULL, 0, &vdi->disk) < 0) { printk(KERN_ERR "invalid initialisation of VD interface\n"); goto exit_err; } if(vboxdd_detect_type(vdi->fd, &disk_type) < 0) goto exit_err; if(vboxdd_open(vdi->disk, disk_type, source, (flags&MS_RDONLY)? VD_OPEN_FLAGS_READONLY:VD_OPEN_FLAGS_NORMAL, NULL) < 0) { errno = ENODEV; printk(KERN_ERR "opening vbox image failed\n"); goto exit_err; } vdi->size = vboxdd_get_size(vdi->disk, 0); vudev_set_devtype(vudev, S_IFBLK); return vdi; exit_err: dlunload_vboxdd(); free(vdi); return NULL; } int vuvdi_fini(void *private_data) { struct vuvdi_t *vdi = private_data; if(vdi) { vboxdd_close(vdi->disk, 0); /* If true, delete the image from the host disk. */ free(vdi); } dlunload_vboxdd(); private_data = NULL; return 0; } struct vudev_operations_t vudev_ops = { .open = vuvdi_open, .close = vuvdi_close, .pread = vuvdi_pread, .pwrite = vuvdi_pwrite, .lseek = vuvdi_lseek, .ioctl = vuvdi_ioctl, .init = vuvdi_init, .fini = vuvdi_fini, }; vuos-0.9.2/vudevfuse/000077500000000000000000000000001476575172100145515ustar00rootroot00000000000000vuos-0.9.2/vudevfuse/CMakeLists.txt000066400000000000000000000007001476575172100173060ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}) set(VU_MOD_TARGET fuse) file(GLOB_RECURSE VUDEV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUDEV_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} stropt volatilestream) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vudevfuse/devfuse.c000066400000000000000000000220121476575172100163530ustar00rootroot00000000000000/* * vudevfuse: /dev/fuse - virtual fuse kernel support * Copyright 2022 Renzo Davoli * Virtualsquare & University of Bologna * * devfuse.c: /dev/fuse I/O management * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include // #define DEBUG static struct vu_stat devfuse_stat = { .st_mode = S_IFCHR | 0666, .st_nlink = 1 }; static uint64_t newunique(struct fusemount_t *fusemount) { fusemount->last_unique += FUSE_REQ_ID_STEP; return fusemount->last_unique; } static size_t iov_total_len(const struct iovec *iov, int iovcnt) { size_t tlen = 0; for (int i = 0; i < iovcnt; i++) tlen += iov[i].iov_len; return tlen; } int vu_devfuse_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate) { (void) flags; (void) sfd; (void) fdprivate; if (pathname[1] != 0) return errno = ENOENT, -1; printkdebug(U,"DEV LSTAT %s", pathname); *buf = devfuse_stat; return 0; } int vu_devfuse_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct fusemount_t *fusemount = calloc(1, sizeof(struct fusemount_t)); (void) pathname; (void) flags; (void) mode; //XXX CK?? if (fusemount == NULL) return errno = ENOMEM, -1; fusemount->ht = NULL; fusemount->uid = geteuid(); fusemount->gid = getegid(); fusemount->fnbuf = fn_init(FUSENODE_BUFSIZE); pthread_mutex_init(&(fusemount->mutex), NULL); fusemount->sem = sem_open(0); *fdprivate = fusemount; printkdebug(U,"DEV OPEN -> %p sem %d", fusemount, fusemount->sem); return 0; } void fusemount_free(struct fusemount_t *fusemount) { int delete; pthread_mutex_lock(&(fusemount->mutex)); delete = fusemount->ht == NULL && fusemount->sem < 0; pthread_mutex_unlock(&(fusemount->mutex)); if (delete) { fn_fini(fusemount->fnbuf); pthread_mutex_destroy(&(fusemount->mutex)); free(fusemount); } } int vu_devfuse_close(int fd, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; printkdebug(U,"DEV CLOSE -> %p", fusemount); if (fusemount->sem >= 0) sem_close(fusemount->sem); fusemount->sem = -1; fusemount_free(fusemount); return 0; } int vu_devfuse_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; //printkdebug(U,"DEV EPOLL -> %d %d sem %d", op, event ? event->events : -1, fusemount->sem); int retval = epoll_ctl(epfd, op, fusemount->sem, event); return retval; } #ifdef DEBUG static void dump(const char *title, const uint8_t *data, size_t bufsize, ssize_t len) { ssize_t line, i; /* out format: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 01234567890123456789012345678901234567890123456789012345678901234 */ char hexbuf[48]; char charbuf[17]; printk("%s size %zd len %zd:\n", title, bufsize, len); if (bufsize > 0 && len > 0) { for (line = 0; line < len; line += 16) { for (i = 0; i < 16; i++) { ssize_t pos = line + i; if (pos < len) { sprintf(hexbuf + (3 * i), "%02x ", data[pos]); charbuf[i] = data[pos] >= ' ' && data[pos] <= '~' ? data[pos] : '.'; } else { sprintf(hexbuf + (3 * i), " "); charbuf[i] = ' '; } } charbuf[i] = 0; printk(" %s %s\n", hexbuf, charbuf); } } } #endif static ssize_t vu_devfuse_read_init(int fd, void *buf, size_t count, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; sem_P(fusemount->sem); pthread_mutex_lock(&(fusemount->mutex)); struct { struct fuse_in_header reqh; struct fuse_init_in init; } init = { .reqh.len = sizeof(init), .reqh.opcode = FUSE_INIT, .reqh.unique = newunique(fusemount), .reqh.nodeid = FUSE_ROOT_ID, .reqh.uid = fusemount->uid, .reqh.gid = fusemount->gid, .reqh.pid = vu_mod_gettid(), .init.major = FUSE_KERNEL_VERSION, .init.minor = FUSE_KERNEL_MINOR_VERSION, }; pthread_mutex_unlock(&(fusemount->mutex)); if (count < sizeof(init)) return errno = EINVAL, -1; #ifdef DEBUG dump("vu_devfuse_read init", buf, count, sizeof(init)); #endif memcpy(buf, &init, sizeof(init)); printkdebug(U, "DEV READ INIT maj %d min %d flags %x", init.init.major, init.init.minor, init.init.flags); return sizeof(init); } static ssize_t vu_devfuse_write_init(int fd, const void *buf, size_t count, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; const struct fuse_out_header *outh = buf; size_t init_out_len = count - sizeof(*outh); // printk("vu_devfuse_write_init init_out_len %d %d\n", init_out_len, outh->error); if (init_out_len < 8) return errno = EINVAL, -1; if (init_out_len > sizeof(fusemount->initdata)) init_out_len = sizeof(fusemount->initdata); #ifdef DEBUG dump("vu_devfuse_write init", buf, count, count); #endif pthread_mutex_lock(&(fusemount->mutex)); memcpy(&fusemount->initdata, outh+1, init_out_len); printkdebug(U, "DEV WRITE INIT maj %d min %d flags %x", fusemount->initdata.major, fusemount->initdata.minor, fusemount->initdata.flags); pthread_mutex_unlock(&(fusemount->mutex)); return count; } ssize_t vu_devfuse_read(int fd, void *buf, size_t count, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; printkdebug(U,"DEV READ %p -> %d", fusemount, count); if (fusemount->initdata.major == 0) return vu_devfuse_read_init(fd, buf, count, fdprivate); sem_P(fusemount->sem); // printk("sem ok\n"); pthread_mutex_lock(&(fusemount->mutex)); struct fusereq *req = fusereq_dequeue(&fusemount->reqq); // printk("read req %p\n", req); if (req == NULL) { // umount-> EOF on dev // the fuse library uses several threads (workers) // ENODEV must be returned to *all* the pending read reqs sem_V(fusemount->sem); pthread_mutex_unlock(&(fusemount->mutex)); return errno = ENODEV, -1; } struct fuse_in_header *h = buf; *h = req->reqh; uint8_t *data = (void *) (h + 1); size_t datalen = count; if (h->len < datalen) datalen = h->len; datalen -= sizeof(*h); for (int i = 0; i < req->reqcnt && datalen > 0; i++) { size_t len = req->reqiov[i].iov_len; if (datalen < len) len = datalen; memcpy(data, req->reqiov[i].iov_base, len); data += len; datalen -= len; } if (req->replycnt == FUSE_NOREPLY) sem_V(req->sem); else fusereq_enqueue(req, &fusemount->replyq); pthread_mutex_unlock(&(fusemount->mutex)); #ifdef DEBUG dump("vu_devfuse_read", buf, count, h->len); #endif return h->len; } ssize_t vu_devfuse_write(int fd, const void *buf, size_t count, void *fdprivate) { (void) fd; struct fusemount_t *fusemount = fdprivate; printkdebug(U,"DEV WRITE %p -> %d", fusemount, count); if (fusemount->initdata.major == 0) return vu_devfuse_write_init(fd, buf, count, fdprivate); const struct fuse_out_header *h = buf; #ifdef DEBUG dump("vu_devfuse_write", buf, count, count); #endif pthread_mutex_lock(&(fusemount->mutex)); struct fusereq *req = fusereq_outqueue(h->unique & ~FUSE_INT_REQ_BIT, &fusemount->replyq); // printk("write req %p\n", req); // check!= NULL req->error = h->error; if (h->error >= 0) { uint8_t *data = (void *) (h + 1); size_t datalen = count; if (h->len < datalen) datalen = h->len; datalen -= sizeof(*h); for (int i = 0; i < req->replycnt && datalen > 0; i++) { size_t len = req->replyiov[i].iov_len; if (datalen < len) len = datalen; memcpy(req->replyiov[i].iov_base, data, len); req->replydatalen = datalen; data += len; datalen -= len; } } sem_V(req->sem); pthread_mutex_unlock(&(fusemount->mutex)); return count; } int32_t vu_devfuse_conversation(struct fusemount_t *fusemount, uint32_t opcode, uint64_t nodeid, struct iovec *reqiov, int reqcnt, struct iovec *replyiov, int replycnt, size_t *return_len) { struct fusereq req = { .sem = sem_open(0), .reqh.len = sizeof(struct fuse_in_header) + iov_total_len(reqiov, reqcnt), .reqh.opcode = opcode, .reqh.unique = newunique(fusemount), .reqh.nodeid = nodeid, .reqh.uid = fusemount->uid, .reqh.gid = fusemount->gid, .reqh.pid = vu_mod_gettid(), .reqiov = reqiov, .reqcnt = reqcnt, .replyiov = replyiov, .replycnt = replycnt }; //printk("c0\n"); pthread_mutex_lock(&(fusemount->mutex)); fusereq_enqueue(&req, &fusemount->reqq); sem_V(fusemount->sem); pthread_mutex_unlock(&(fusemount->mutex)); //printk("c1\n"); sem_P(req.sem); sem_close(req.sem); //printk("c2\n"); if (return_len != NULL) *return_len = req.replydatalen; return req.error; } vuos-0.9.2/vudevfuse/devfuse.h000066400000000000000000000026141476575172100163660ustar00rootroot00000000000000#ifndef _DEVFUSE_H #define _DEVFUSE_H #include #include #include #define FUSE_INT_REQ_BIT (1ULL << 0) #define FUSE_REQ_ID_STEP (1ULL << 1) #define FUSE_NOREPLY (-1) int vu_devfuse_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate); int vu_devfuse_open(const char *pathname, int flags, mode_t mode, void **fdprivate); int vu_devfuse_close(int fd, void *fdprivate); int vu_devfuse_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, void *fdprivate); ssize_t vu_devfuse_read(int fd, void *buf, size_t count, void *fdprivate); ssize_t vu_devfuse_write(int fd, const void *buf, size_t count, void *fdprivate); #define vu_devfuse_nosys(...) (errno = ENOSYS, -1) int32_t vu_devfuse_conversation(struct fusemount_t *fusemount, uint32_t opcode, uint64_t nodeid, struct iovec *reqiov, int reqcnt, struct iovec *replyiov, int replycnt, size_t *return_len); void fusemount_free(struct fusemount_t *fusemount); #define IOV0 NULL, 0 #define IOV_NOREPLY NULL, FUSE_NOREPLY #define IOV1(BASE, LEN) (struct iovec []) {{(void *) (BASE), (LEN)}}, 1 #define IOV2(BASE0, LEN0, BASE1, LEN1) \ (struct iovec []) {{(void *) (BASE0), (LEN0)}, {(void *) (BASE1), (LEN1)}}, 2 #define IOV3(BASE0, LEN0, BASE1, LEN1, BASE2, LEN2) \ (struct iovec []) { \ {(void *) (BASE0), (LEN0)}, \ {(void *) (BASE1), (LEN1)}, \ {(void *) (BASE2), (LEN2)}}, 3 #endif vuos-0.9.2/vudevfuse/eventsem.h000066400000000000000000000007131476575172100165510ustar00rootroot00000000000000#ifndef _EVENT_SEM_H #define _EVENT_SEM_H #include #include static inline int sem_open(int init) { return eventfd(init, EFD_SEMAPHORE | EFD_CLOEXEC); } static inline size_t sem_P(int fd) { uint64_t dummy; return read(fd, &dummy, sizeof(dummy)); } static inline size_t sem_V(int fd) { static const uint64_t one = 1LL; return write(fd, &one, sizeof(one)); } static inline int sem_close(int fd) { return close(fd); } #endif vuos-0.9.2/vudevfuse/fusenode.c000066400000000000000000000167251476575172100165400ustar00rootroot00000000000000/* * vudevfuse: /dev/fuse - virtual fuse kernel support * Copyright 2022 Renzo Davoli * Virtualsquare & University of Bologna * * fusenode.c: cache of nodeid + stat data * * 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, see . * */ #include #include #include #include #include #include #include #include #include #define FUSENODE_HT_SIZE 64 #define FUSENODE_HT_MASK (FUSENODE_HT_SIZE - 1) struct fusenode { struct list_head pathlink; struct list_head nodelink; struct list_head lrulink; uint64_t hash; uint64_t nodeid; uint64_t generation; struct timespec entry_expire; struct timespec attr_expire; struct fuse_attr attr; uint32_t nlookup; char path[]; }; struct fusenode_buf { pthread_mutex_t mutex; int count; int maxlru; struct list_head path_ht[FUSENODE_HT_SIZE]; struct list_head node_ht[FUSENODE_HT_SIZE]; struct list_head lru; }; static const struct fuse_attr null_attr; static inline uint64_t fusenode_hashsum(const char *path) { /* djb2 */ uint64_t sum = 5381; for (; *path ; path++) sum = ((sum << 5) + sum) + *path; /* hash * 33 + c */ return sum; } static void expiretime (struct timespec *expire, struct timespec *now, uint64_t valid, uint32_t valid_nsec) { expire->tv_sec = now->tv_sec + valid; expire->tv_nsec = now->tv_nsec + valid_nsec; while (expire->tv_nsec > 1000000000L) { expire->tv_nsec -= 1000000000L; expire->tv_sec++; } } static int timespec_cmp(struct timespec *a, struct timespec *b) { if (a->tv_sec > b->tv_sec) return 1; if (a->tv_sec < b->tv_sec) return -1; if (a->tv_nsec > b->tv_nsec) return 1; if (a->tv_nsec < b->tv_nsec) return -1; return 0; } uint64_t fn_get(struct fusenode_buf *ht, const char *path, struct fuse_attr *attr) { uint64_t hash = fusenode_hashsum(path); uint64_t nodeid = 0; struct fusenode *scan; pthread_mutex_lock(&ht->mutex); list_for_each_entry(scan, &ht->path_ht[hash & FUSENODE_HT_MASK], pathlink) { //printf("GET |%s| |%s| %lld %lld\n", path, scan->path, hash, scan->hash); if (hash == scan->hash && strcmp(path, scan->path) == 0) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); //printf("timecmp +++ %d \n", timespec_cmp(&now, &scan->entry_expire)); if (timespec_cmp(&now, &scan->entry_expire) > 0) break; if (attr) { if (timespec_cmp(&now, &scan->attr_expire) > 0) *attr = null_attr; else *attr = scan->attr; //printf("timecmp >>> %d \n", timespec_cmp(&now, &scan->attr_expire)); } list_del(&scan->lrulink); list_add(&scan->lrulink, &ht->lru); nodeid = scan->nodeid; break; } } pthread_mutex_unlock(&ht->mutex); //printf("fn_get %s %d %d\n", path, nodeid, hash & FUSENODE_HT_MASK); return nodeid; } void fn_add(struct fusenode_buf *ht, const char *path, struct fuse_entry_out *entry) { struct fusenode *scan; struct fusenode *this = NULL; uint64_t hash = fusenode_hashsum(path); //printf("ADD |%s| %d\n", path, hash & FUSENODE_HT_MASK); pthread_mutex_lock(&ht->mutex); list_for_each_entry(scan, &ht->path_ht[hash & FUSENODE_HT_MASK], pathlink) { if (hash == scan->hash && strcmp(path, scan->path) == 0 && scan->nodeid == entry->nodeid && scan->generation == entry->generation) { // may nodeid change? list_del(&scan->lrulink); this = scan; break; } } if (this == NULL) { this = calloc(1, sizeof(struct fusenode) + strlen(path) + 1); if (this != NULL) { this->nlookup = 0; this->hash = hash; this->nodeid = entry->nodeid; this->generation = entry->generation; strcpy(this->path, path); list_add(&this->pathlink, &ht->path_ht[hash & FUSENODE_HT_MASK]); list_add(&this->nodelink, &ht->node_ht[entry->nodeid & FUSENODE_HT_MASK]); ht->count++; } } if (this != NULL) { struct timespec now; this->nlookup++; this->attr = entry->attr; clock_gettime(CLOCK_REALTIME, &now); expiretime(&this->entry_expire, &now, entry->entry_valid, entry->entry_valid_nsec); expiretime(&this->attr_expire, &now, entry->attr_valid, entry->attr_valid_nsec); list_add(&this->lrulink, &ht->lru); } pthread_mutex_unlock(&ht->mutex); } uint64_t fn_getnode(struct fusenode_buf *ht, uint64_t nodeid, struct fuse_attr *attr) { uint64_t rnodeid = 0; struct fusenode *scan; pthread_mutex_lock(&ht->mutex); list_for_each_entry(scan, &ht->node_ht[nodeid & FUSENODE_HT_MASK], nodelink) { if (nodeid == scan->nodeid) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); if (timespec_cmp(&now, &scan->entry_expire) > 0) break; if (timespec_cmp(&now, &scan->attr_expire) > 0) *attr = null_attr; else *attr = scan->attr; list_del(&scan->lrulink); list_add(&scan->lrulink, &ht->lru); rnodeid = nodeid; break; } } pthread_mutex_unlock(&ht->mutex); return rnodeid; } void fn_updatenode(struct fusenode_buf *ht, uint64_t nodeid, struct fuse_attr_out *entry) { struct fusenode *scan; pthread_mutex_lock(&ht->mutex); list_for_each_entry(scan, &ht->node_ht[nodeid & FUSENODE_HT_MASK], nodelink) { if (nodeid == scan->nodeid) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); expiretime(&scan->attr_expire, &now, entry->attr_valid, entry->attr_valid_nsec); scan->attr = entry->attr; break; } } pthread_mutex_unlock(&ht->mutex); } static void _fn_delnode(struct fusenode *old) { list_del(&old->pathlink); list_del(&old->nodelink); list_del(&old->lrulink); free(old); } uint64_t fn_delnode(struct fusenode_buf *ht, uint64_t nodeid, uint64_t *nlookup) { uint64_t rnodeid = 0; struct fusenode *scan; pthread_mutex_lock(&ht->mutex); list_for_each_entry(scan, &ht->node_ht[nodeid & FUSENODE_HT_MASK], nodelink) { if (nodeid == scan->nodeid) { rnodeid = nodeid; *nlookup = scan->nlookup; ht->count--; _fn_delnode(scan); break; } } pthread_mutex_unlock(&ht->mutex); return rnodeid; } uint64_t fn_forgetlru(struct fusenode_buf *ht, uint64_t *nlookup) { uint64_t nodeid = 0; pthread_mutex_lock(&ht->mutex); if (ht->count > ht->maxlru) { struct fusenode *last = list_last_entry(&ht->lru, struct fusenode, lrulink); nodeid = last->nodeid; *nlookup = last->nlookup; ht->count--; _fn_delnode(last); } pthread_mutex_unlock(&ht->mutex); return nodeid; } struct fusenode_buf *fn_init(int maxlru) { struct fusenode_buf *new = calloc(1, sizeof(struct fusenode_buf)); if (new) { pthread_mutex_init(&(new->mutex), NULL); struct fuse_entry_out root = { .nodeid = FUSE_ROOT_ID, .entry_valid = INT64_MAX >> 1 // far away in the future w/o overflow }; new->maxlru = maxlru; for (int i = 0; i < FUSENODE_HT_SIZE; i++) { INIT_LIST_HEAD(&new->path_ht[i]); INIT_LIST_HEAD(&new->node_ht[i]); } INIT_LIST_HEAD(&new->lru); fn_add(new, "", &root); } return new; } void fn_fini(struct fusenode_buf *ht) { while(!list_empty(&ht->lru)) _fn_delnode(list_first_entry(&ht->lru, struct fusenode, lrulink)); pthread_mutex_destroy(&(ht->mutex)); free(ht); } vuos-0.9.2/vudevfuse/fusenode.h000066400000000000000000000013111476575172100165260ustar00rootroot00000000000000#ifndef _FUSENODE_H #define _FUSENODE_H #include #include struct fusenode_buf; uint64_t fn_get(struct fusenode_buf *ht, const char *path, struct fuse_attr *attr); void fn_add(struct fusenode_buf *ht, const char *path, struct fuse_entry_out *entry); uint64_t fn_getnode(struct fusenode_buf *ht, uint64_t nodeid, struct fuse_attr *attr); void fn_updatenode(struct fusenode_buf *ht, uint64_t nodeid, struct fuse_attr_out *entry); uint64_t fn_delnode(struct fusenode_buf *ht, uint64_t nodeid, uint64_t *nlookup); uint64_t fn_forgetlru(struct fusenode_buf *ht, uint64_t *nlookup); struct fusenode_buf *fn_init(int maxlru); void fn_fini(struct fusenode_buf *ht); #endif vuos-0.9.2/vudevfuse/fusereqq.c000066400000000000000000000034471476575172100165600ustar00rootroot00000000000000/* * vudevfuse: /dev/fuse - virtual fuse kernel support * Copyright 2022 Renzo Davoli * Virtualsquare & University of Bologna * * fusereqq.c: manage request queues: * store pending requests to the user level daemon * and match replies * * 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, see . * */ #include void fusereq_enqueue(struct fusereq *req, struct fusereq **tail) { struct fusereq *last = *tail; if (last == NULL) req->next = req; else { req->next = last->next; last->next = req; } *tail = req; } struct fusereq *fusereq_dequeue(struct fusereq **tail) { struct fusereq *last = *tail; if (last == NULL) return NULL; struct fusereq *first = last->next; if (last == first) *tail = NULL; else last->next = first->next; first->next = NULL; return first; } struct fusereq *fusereq_outqueue(uint64_t unique, struct fusereq **tail) { struct fusereq *prev, *this; for (prev = *tail, this = prev->next; this != *tail; prev = this, this = prev->next) if (this->reqh.unique == unique) break; if (this->reqh.unique == unique) { if (this == *tail) *tail = (prev == *tail) ? NULL : prev; prev->next = this->next; return this; } else return NULL; } vuos-0.9.2/vudevfuse/fusereqq.h000066400000000000000000000007751476575172100165660ustar00rootroot00000000000000#ifndef _FUSEREQQ_H #define _FUSEREQQ_H #include #include #include struct fusereq { struct fusereq *next; int sem; struct fuse_in_header reqh; uint32_t error; struct iovec *reqiov; int reqcnt; struct iovec *replyiov; int replycnt; size_t replydatalen; }; void fusereq_enqueue(struct fusereq *req, struct fusereq **tail); struct fusereq *fusereq_dequeue(struct fusereq **tail); struct fusereq *fusereq_outqueue(uint64_t unique, struct fusereq **tail); #endif vuos-0.9.2/vudevfuse/listx.h000066400000000000000000000050641476575172100160720ustar00rootroot00000000000000/* Doubly linked list macros compatible with Linux kernel's version */ #ifndef _LISTX_H #define _LISTX_H #include // NULL + offsetof #define container_of(ptr, type, member) \ ((type *)(((char *)(ptr)) - offsetof(type,member))) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; } static inline int list_empty(const struct list_head *head) { return head->next == head; } static inline struct list_head *list_next(const struct list_head *current) { if (list_empty(current)) return NULL; else return current->next; } static inline struct list_head *list_prev(const struct list_head *current) { if (list_empty(current)) return NULL; else return current->prev; } #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) #define list_for_each_entry(pos, head, member) \ for (pos = container_of((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = container_of(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_reverse(pos, head, member) \ for (pos = container_of((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = container_of(pos->member.prev, typeof(*pos), member)) #define list_first_entry(head, type, member) \ ((list_empty(head)) ? NULL : container_of((head)->next, type, member)) #define list_last_entry(head, type, member) \ ((list_empty(head)) ? NULL : container_of((head)->prev, type, member)) #endif vuos-0.9.2/vudevfuse/vudevfuse.c000066400000000000000000000121741476575172100167360ustar00rootroot00000000000000/* * vudevfuse: /dev/fuse - virtual fuse kernel support * Copyright 2022 Renzo Davoli * Virtualsquare & University of Bologna * * vudevfuse.c: main file of the module. init/fini mount/umount * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include struct vuht_entry_t *devfuse_ht; VU_PROTOTYPES(fuse) struct vu_module_t vu_module = { .name = "fuse", .description = "vu fuse device", .flags = VUDEVFUSE_MODULE_FLAGS }; static int parse_mount_fd(const char *input) { int fd = -1; int tagc = stropt(input, NULL, NULL, 0); if(tagc > 1) { char buf[strlen(input)+1]; char *tags[tagc]; char *args[tagc]; stropt(input, tags, args, buf); for (int i=0; tags[i] != NULL; i++) { if (args[i]) { switch(strcase(tags[i])) { case STRCASE(f,d): fd = strtoul(args[i], NULL, 0); break; } } } } return fd; } static void parse_mount_options(struct fusemount_t *fusemount, const char *input) { int tagc = stropt(input, NULL, NULL, 0); if(tagc > 1) { char buf[strlen(input)+1]; char *tags[tagc]; char *args[tagc]; stropt(input, tags, args, buf); for (int i=0; tags[i] != NULL; i++) { if (args[i]) { switch(strcase(tags[i])) { case STRCASE(f,d): break; case STRCASE(r,o,o,t,m,o,d,e): fusemount->rootmode = strtoul(args[i], NULL, 8); break; case STRCASE(u,s,e,r,underscore,i,d): fusemount->uid = strtoul(args[i], NULL, 0); break; case STRCASE(g,r,o,u,p,underscore,i,d): fusemount->gid = strtoul(args[i], NULL, 0); break; } } else { switch(strcase(tags[i])) { #if 0 case STRCASE(c,h,r,d,e,v): break; #endif } } } } } static int fusemount_confirm(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { (void) type; (void) arg; (void) arglen; struct fusemount_t *fusemount = vuht_get_private_data(ht); //printk("fusemount_confirm %p\n", fusemount); pthread_mutex_lock(&(fusemount->mutex)); int retvalue = fusemount->initdata.major > 0; pthread_mutex_unlock(&(fusemount->mutex)); return retvalue; } int vu_fuse_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vu_service_t *s = vu_mod_getservice(); int fd; struct vuht_entry_t *fdht; struct fusemount_t *fusemount; if (data == NULL || (fd = parse_mount_fd(data)) < 0 || ((fdht = vu_mod_fd_get_ht(fd)) != devfuse_ht) || (vu_mod_fd_get_sfd(fd, (void **) &fusemount) < 0) || fusemount == NULL) return errno = EFAULT, -1; if (fusemount->ht != NULL) return errno = EBUSY, -1; printkdebug(U,"MOUNT source:%s target:%s type:%s flags:0x%x data:%s", source, target, filesystemtype, mountflags, data); pthread_mutex_lock(&(fusemount->mutex)); fusemount->mountflags = mountflags; parse_mount_options(fusemount, data); fusemount->ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, fusemount_confirm, fusemount); pthread_mutex_unlock(&(fusemount->mutex)); sem_V(fusemount->sem); return 0; } int vu_fuse_umount2(const char *target, int flags) { struct fusemount_t *fusemount = vu_get_ht_private_data(); if (fusemount == NULL) { errno = EINVAL; return -1; } else { int retval; sem_V(fusemount->sem); pthread_mutex_lock(&(fusemount->mutex)); if ((retval = vuht_del(fusemount->ht, flags)) == 0) fusemount->ht = NULL; pthread_mutex_unlock(&(fusemount->mutex)); printkdebug(U,"UMOUNT target:%s flags:%d %p = %d", target, flags, fusemount, retval); return retval; } } void vu_fuse_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { (void) type; (void) arg; (void) arglen; (void) ht; struct fusemount_t *fusemount = vu_get_ht_private_data(); printkdebug(U,"CLEANUP %p", fusemount); if (fusemount != NULL) fusemount_free(fusemount); } void *vu_fuse_init(void) { struct vu_service_t *s = vu_mod_getservice(); printkdebug(U,"INIT"); devfuse_ht = vuht_pathadd(CHECKPATH,"none","/dev/fuse","devfuse",0,"",s,0,NULL,NULL); return NULL; } int vu_fuse_fini(void *private) { (void) private; if (devfuse_ht != NULL) { printkdebug(U,"FINI"); return vuht_del(devfuse_ht, MNT_FORCE); } return 0; } __attribute__((constructor)) static void fuse_init(void) { debug_set_name(U, "+FUSE"); } __attribute__((destructor)) static void fuse_fini(void) { debug_set_name(U, ""); } vuos-0.9.2/vudevfuse/vudevfuse.h000066400000000000000000000013011476575172100167310ustar00rootroot00000000000000#ifndef _VUDEVFUSE_H #define _VUDEVFUSE_H #include #include #include #include #include #include #define VUDEVFUSE_MODULE_FLAGS (VU_USE_PRW) #define FUSENODE_BUFSIZE 256 extern struct vuht_entry_t *devfuse_ht; #ifndef FUSE_SUPER_MAGIC #define FUSE_SUPER_MAGIC 0x65735546 #endif struct fusemount_t { struct vuht_entry_t *ht; pthread_mutex_t mutex; int sem; // < 0 if fd is closed unsigned long mountflags; mode_t rootmode; // unsupported uid_t uid; uid_t gid; uint64_t last_unique; struct fuse_init_out initdata; struct fusereq *reqq; struct fusereq *replyq; struct fusenode_buf *fnbuf; // mountflags }; #endif vuos-0.9.2/vudevfuse/vudevfuse_ops.c000066400000000000000000000725531476575172100176260ustar00rootroot00000000000000/* * vudevfuse: /dev/fuse - virtual fuse kernel support * Copyright 2022 Renzo Davoli * Virtualsquare & University of Bologna * * vudevfuse_ops.c: manage calls from user processes * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _O_LARGEFILE 0x8000 #define _O_OPENONLY (O_CREAT | O_EXCL | O_TRUNC) VU_PROTOTYPES(fuse) struct fusefile_t { pthread_mutex_t mutex; uint64_t nodeid; mode_t filemode; uint64_t fh; int flags; uint32_t open_flags; FILE *dir; }; // for lstat parent arg: #define LSTAT_THIS 0 #define LSTAT_PARENT 1 /*heuristics for file system which does not set st_ino */ static inline unsigned long hash_inodeno (const char *s) { unsigned long sum = 0; while (*s) { sum = sum ^ ((sum << 5) + (sum >> 2) + *s); s++; } return sum; } /*heuristics for file system which does not set st_dev */ static dev_t fake_dev(void *addr) { return (dev_t)(((unsigned long) addr) >> 3); } static void fuse2stat(struct vu_stat *buf, struct fuse_attr *fa) { memset(buf, 0, sizeof(*buf)); buf->st_ino = fa->ino; buf->st_size = fa->size; buf->st_blocks = fa->blocks; buf->st_atime = fa->atime; buf->st_mtime = fa->mtime; buf->st_ctime = fa->ctime; buf->st_atim.tv_nsec = fa->atimensec; buf->st_mtim.tv_nsec = fa->mtimensec; buf->st_ctim.tv_nsec = fa->ctimensec; buf->st_mode = fa->mode; buf->st_nlink = fa->nlink; buf->st_uid = fa->uid; buf->st_gid = fa->gid; buf->st_rdev = fa->rdev; buf->st_blksize = fa->blksize; } static void fuse2statfs(struct statfs *buf, struct fuse_kstatfs *fs) { buf->f_type = FUSE_SUPER_MAGIC; buf->f_blocks = fs->blocks; buf->f_bfree = fs->bfree; buf->f_bavail = fs->bavail; buf->f_files = fs->files; buf->f_ffree = fs->ffree; buf->f_bsize = fs->bsize; buf->f_namelen = fs->namelen; buf->f_frsize = fs->frsize; } #define err_return_unlock(MUTEX, ERRNO) \ do { \ pthread_mutex_unlock(MUTEX); \ errno = (ERRNO); \ return -1; \ } while (0); static const char *fuse_basename(const char *path) { const char *basename = strrchr(path, '/'); if (basename == NULL || basename[1] == 0) return NULL; return basename + 1; } static int fuse_getstat(struct fusemount_t *fusemount, char *path, int dirlen, struct fuse_attr *attr, uint64_t dirid, uint64_t *nodeid) { char *name = path + dirlen + 1; //printf("path |%s| dir |%.*s| name |%s| %p\n", path, dirlen, path, name, nodeid); *nodeid = fn_get(fusemount->fnbuf, path, attr); if (*nodeid == 0) { struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_LOOKUP, dirid, IOV1(name, strlen(name) + 1), IOV1(&entryout, sizeof(entryout)), NULL); if (err < 0) return errno = -err, -1; *nodeid = entryout.nodeid; fn_add(fusemount->fnbuf, path, &entryout); if (attr) *attr = entryout.attr; } else if (attr != NULL && attr->mode == 0) { struct fuse_getattr_in attrin = { 0 }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_GETATTR, *nodeid, IOV1(&attrin, sizeof(attrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, *nodeid, &attrout); *attr = attrout.attr; } return 0; } static int fuse_recstat(struct fusemount_t *fusemount, char *path, struct fuse_attr *attr, uint64_t *nodeid) { char *slash = strrchr(path, '/'); // printf("rec %s\n", path); if (slash) { uint64_t dirid; *slash = 0; if (fuse_recstat(fusemount, path, NULL, &dirid) < 0) return -1; *slash = '/'; return fuse_getstat(fusemount, path, slash - path, attr, dirid, nodeid); } else return fuse_getstat(fusemount, path, 0, attr, FUSE_ROOT_ID, nodeid); } static int path_len(const char *path, int parent) { if (parent) { char *slash = strrchr(path, '/'); if (slash) return slash - path; else return 0; } else return strlen(path); } static int fuse_lstat(struct fusemount_t *fusemount, const char *path, struct vu_stat *buf, uint64_t *nodeid, int parent) { int pathlen = path_len(path, parent); char pathbuf[pathlen + 1]; sprintf(pathbuf, "%.*s", pathlen, path); if (pathbuf[1] == 0) pathbuf[0] = 0; // '/' exception uint64_t rnodeid; struct fuse_attr attr; int staterr = fuse_recstat(fusemount, pathbuf, &attr, &rnodeid); if (staterr < 0) return staterr; if (nodeid) *nodeid = rnodeid; if (buf) { fuse2stat(buf, &attr); if (buf->st_ino == 0) buf->st_ino = (ino_t)hash_inodeno(path); if (buf->st_dev == 0) buf->st_dev = fake_dev(fusemount); } return 0; } static void fuse_invalidate_attr(struct fusemount_t *fusemount, uint64_t nodeid) { struct fuse_attr_out attrout = { .attr_valid = -1 }; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); } int vu_fuse_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_lstat(pathname, buf, flags, sfd, fdprivate); printkdebug(U,"LSTAT %s", pathname); struct fusemount_t *fusemount = vuht_get_private_data(ht); int retval = fuse_lstat(fusemount, pathname, buf, NULL, LSTAT_THIS); /* LRU cleaning of fnbuf */ uint64_t nodeid, nlookup; while ((nodeid = fn_forgetlru(fusemount->fnbuf, &nlookup)) > 0) { struct fuse_forget_in forgetin = { .nlookup = nlookup }; vu_devfuse_conversation(fusemount, FUSE_FORGET, nodeid, IOV1(&forgetin, sizeof(forgetin)), IOV_NOREPLY, NULL); } return retval; } int vu_fuse_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_open(pathname, flags, mode, fdprivate); flags |= _O_LARGEFILE; printkdebug(U,"OPEN path:%s flags:0x%x", pathname, flags); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (((flags & O_ACCMODE) != O_RDONLY) && (fusemount->mountflags & MS_RDONLY)) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; mode_t filemode = 0; struct fusefile_t *fusefile = malloc(sizeof(*fusefile)); if (fusefile == NULL) return errno = ENOMEM, -1; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); struct fuse_open_out openout; if (staterr < 0 && errno == ENOENT && (flags & O_CREAT)) { uint64_t dirid; staterr = fuse_lstat(fusemount, pathname, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return free(fusefile), -1; const char *basename = fuse_basename(pathname); if (basename == NULL) return free(fusefile), errno = EINVAL, -1; struct fuse_create_in createin = { .flags = flags, .mode = S_IFREG | mode, .umask = vu_mod_getumask() }; struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_CREATE, dirid, IOV2(&createin, sizeof(createin), basename, strlen(basename) + 1), IOV2(&entryout, sizeof(entryout), &openout, sizeof(openout)), NULL); if (err < 0) return free(fusefile), errno = -err, -1; nodeid = entryout.nodeid; filemode = entryout.attr.mode; fn_add(fusemount->fnbuf, pathname, &entryout); flags &= ~_O_OPENONLY; } else { if (staterr < 0) return free(fusefile), -1; filemode = statbuf.st_mode; if ((flags & O_DIRECTORY) && (!S_ISDIR(filemode))) return free(fusefile), errno = ENOTDIR, -1; if ((flags & O_ACCMODE) != O_RDONLY && (S_ISDIR(filemode))) return free(fusefile), errno = EISDIR, -1; int fuse_open_opendir = (S_ISDIR(filemode)) ? FUSE_OPENDIR : FUSE_OPEN; /* check flags for opendir? O_RDONLY */ int trunc = flags & O_TRUNC; flags &= ~_O_OPENONLY; struct fuse_open_in openin = { .flags = flags }; int err = vu_devfuse_conversation(fusemount, fuse_open_opendir, nodeid, IOV1(&openin, sizeof(openin)), IOV1(&openout, sizeof(openout)), NULL); if (err < 0) return free(fusefile), errno = -err, -1; if (trunc) { struct fuse_setattr_in setattrin = { .valid = FATTR_SIZE, .size = 0 }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_SETATTR, nodeid, IOV1(&setattrin, sizeof(setattrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); } } pthread_mutex_init(&(fusefile->mutex), NULL); fusefile->nodeid = nodeid; fusefile->filemode = filemode; fusefile->fh = openout.fh; fusefile->flags = flags; fusefile->open_flags = openout.open_flags; fusefile->dir = NULL; *fdprivate = fusefile; return 0; } int vu_fuse_close(int fd, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_close(fd, fdprivate); printkdebug(U,"CLOSE %d", fd); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct fusefile_t *fusefile = fdprivate; /* XXX */ if (fusefile == NULL) { printk("ERR vu_fuse_close\n"); return errno = EIO, -1; } pthread_mutex_lock(&(fusefile->mutex)); int fuse_rel_reldir = (S_ISDIR(fusefile->filemode)) ? FUSE_RELEASEDIR : FUSE_RELEASE; struct fuse_release_in releasein = { .fh = fusefile->fh, .flags = fusefile->flags }; int err = vu_devfuse_conversation(fusemount, fuse_rel_reldir, fusefile->nodeid, IOV1(&releasein, sizeof(releasein)), IOV0, NULL); if (err < 0) err_return_unlock(&(fusefile->mutex), -err); if (fusefile->dir != NULL) fclose(fusefile->dir); fuse_invalidate_attr(fusemount, fusefile->nodeid); pthread_mutex_unlock(&(fusefile->mutex)); pthread_mutex_destroy(&(fusefile->mutex)); free(fusefile); return 0; } int vu_fuse_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_epoll_ctl(epfd, op, fd, event,fdprivate); return errno = ENOSYS, -1; } ssize_t vu_fuse_read(int fd, void *buf, size_t count, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_read(fd, buf, count, fdprivate); return vu_devfuse_nosys(); } ssize_t vu_fuse_write(int fd, const void *buf, size_t count, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_write(fd, buf, count, fdprivate); return vu_devfuse_nosys(); } static void fuse_filldir(FILE *f, const char *name, unsigned short int namelen, unsigned char type, __ino64_t ino) { /* glibc hides enries having d_ino == 0 */ struct dirent64 entry = { .d_ino = ino == 0 ? (ino_t) -1 : ino, .d_type = type, .d_off = ftello(f), }; static char filler[7]; unsigned short int reclen = offsetof(struct dirent64, d_name) + namelen + 1; int ret_value; snprintf(entry.d_name, 256, "%.*s", namelen, name); /* entries are always 8 bytes aligned */ entry.d_reclen = (reclen + 7) & (~7); ret_value = fwrite(&entry, reclen, 1, f); /* add a filler to align the next entry */ if (entry.d_reclen > reclen) ret_value += fwrite(filler, entry.d_reclen - reclen, 1, f); } #define FUSE_INBUF_LEN 4080 static void populate_dir(struct fusemount_t *fusemount, struct fusefile_t *fusefile) { if ((fusefile->dir = volstream_open()) == NULL) return; struct fuse_read_in readin = { .fh = fusefile->fh, .size = FUSE_INBUF_LEN, .offset = 0 }; for (;;) { char inbuf[FUSE_INBUF_LEN]; size_t retcount; int err = vu_devfuse_conversation(fusemount, FUSE_READDIR, fusefile->nodeid, IOV1(&readin, sizeof(readin)), IOV1(inbuf, FUSE_INBUF_LEN), &retcount); if (retcount <= 0 || err < 0) break; for (struct fuse_dirent *fde = (void *) inbuf; (char *) fde < (inbuf + retcount); fde = (void *)(((char *)(fde + 1)) + ((fde->namelen + 7) & (~7))) ) { fuse_filldir(fusefile->dir, fde->name, fde->namelen, fde->type, fde->ino); readin.offset = fde->off; } } } int vu_fuse_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *fdprivate) { printkdebug(U,"GETDENTS %d", fd); struct vuht_entry_t *ht = vu_mod_getht(); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct fusefile_t *fusefile = fdprivate; size_t freadout; pthread_mutex_lock(&(fusefile->mutex)); if (fusefile->dir == NULL) { populate_dir(fusemount, fusefile); fseek(fusefile->dir, 0, SEEK_SET); } pthread_mutex_unlock(&(fusefile->mutex)); freadout = fread(dirp, 1, count, fusefile->dir); /* if the buffer is full the last entry might be incomplete. update freadout to drop the last incomplete entry, and seek back the position in the file to reread it from its beginning at the next getdents64 */ if (freadout == count) { unsigned int bpos = 0; struct dirent64 *d; char *buf = (char *) dirp; while (1) { d = (struct dirent64 *) (buf + bpos); if (count - bpos < offsetof(struct dirent64, d_name)) break; if (bpos + d->d_reclen > count) break; bpos += d->d_reclen; } if (bpos < count) { fseeko(fusefile->dir, - (int) (count - bpos), SEEK_CUR); freadout -= count - bpos; } /* the buffer is so short that it does not fit one entry. Return EINVAL! */ if (freadout == 0) { errno = EINVAL; return -1; } } return freadout; } ssize_t vu_fuse_readlink(char *path, char *buf, size_t bufsiz) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"READLINK path:%s", path); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, path, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; // check if symlink size_t retsize; int err = vu_devfuse_conversation(fusemount, FUSE_READLINK, nodeid, IOV0, IOV1(buf, bufsiz), &retsize); if (err < 0) return errno = -err, -1; if (retsize < bufsiz) buf[retsize] = 0; return retsize; } int vu_fuse_statfs (const char *pathname, struct statfs *buf, int fd, void *fdprivate) { (void) pathname; (void) fd; (void) fdprivate; struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"STATFS"); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct fuse_kstatfs fusebuf; int err = vu_devfuse_conversation(fusemount, FUSE_STATFS, FUSE_ROOT_ID, IOV0, IOV1(&fusebuf, sizeof(fusebuf)), NULL); if (err < 0) return errno = -err, -1; fuse2statfs(buf, &fusebuf); return 0; } /* access could be emulated using stat but local uid/gid may not be consistent with Fuse filesytem's uid/gid */ int vu_fuse_access(char *path, int mode, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"ACCESS %s mode=%zd flags=%d", path, mode, flags); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, path, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; struct fuse_access_in accessin = { .mask = mode }; int err = vu_devfuse_conversation(fusemount, FUSE_ACCESS, nodeid, IOV1(&accessin, sizeof(accessin)), IOV0, NULL); if (err < 0) return errno = -err, -1; return 0; } ssize_t vu_fuse_pread64(int fd, void *buf, size_t count, off_t offset, int flags, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"PREAD %d count=%zd offset=%zd", fd, count, offset); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct fusefile_t *fusefile = fdprivate; struct fuse_read_in readin = { .fh = fusefile->fh, .offset = offset, .size = count, .flags = flags }; int err = vu_devfuse_conversation(fusemount, FUSE_READ, fusefile->nodeid, IOV1(&readin, sizeof(readin)), IOV1(buf, count), &count); if (err < 0) return errno = -err, -1; return count; } ssize_t vu_fuse_pwrite64(int fd, const void *buf, size_t count, off_t offset, int flags, void *fdprivate) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"PWRITE %d count=%zd offset=%zd", fd, count, offset); struct fusemount_t *fusemount = vuht_get_private_data(ht); struct fusefile_t *fusefile = fdprivate; if ((fusefile->flags & O_ACCMODE) == O_RDONLY) return errno = EBADF, -1; struct fuse_write_in writein = { .fh = fusefile->fh, .offset = offset, .size = count, .flags = fusefile->flags }; struct fuse_write_out writeout; int err = vu_devfuse_conversation(fusemount, FUSE_WRITE, fusefile->nodeid, IOV2(&writein, sizeof(writein), buf, count), IOV1(&writeout, sizeof(writeout)), NULL); if (err < 0) return errno = -err, -1; count = writeout.size; return count; } int vu_fuse_mkdir(const char *pathname, mode_t mode) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"MKDIR path:%s mode:%x", pathname, mode); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t dirid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(pathname); if (basename == NULL) return errno = EINVAL, -1; struct fuse_mkdir_in mkdirin = { .mode = mode, .umask = vu_mod_getumask() }; struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_MKDIR, dirid, IOV2(&mkdirin, sizeof(mkdirin), basename, strlen(basename) + 1), IOV1(&entryout, sizeof(entryout)), NULL); if (err < 0) return errno = -err, -1; fn_add(fusemount->fnbuf, pathname, &entryout); return 0; } int vu_fuse_mknod(const char *pathname, mode_t mode, dev_t dev) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"MKNOD path:%s mode:%x dev=%x", pathname, mode, dev); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t dirid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(pathname); if (basename == NULL) return errno = EINVAL, -1; struct fuse_mknod_in mknodin = { .mode = mode, .rdev = dev, .umask = vu_mod_getumask() }; struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_MKNOD, dirid, IOV2(&mknodin, sizeof(mknodin), basename, strlen(basename) + 1), IOV1(&entryout, sizeof(entryout)), NULL); if (err < 0) return errno = -err, -1; fn_add(fusemount->fnbuf, pathname, &entryout); return 0; } int vu_fuse_unlink(const char *pathname) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"UNLINK path:%s", pathname); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; uint64_t dirid; staterr = fuse_lstat(fusemount, pathname, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(pathname); if (basename == NULL) return errno = EINVAL, -1; int err = vu_devfuse_conversation(fusemount, FUSE_UNLINK, dirid, IOV1(basename, strlen(basename) + 1), IOV0, NULL); if (err < 0) return errno = -err, -1; uint64_t nlookup; if (fn_delnode(fusemount->fnbuf, nodeid, &nlookup) > 0) { struct fuse_forget_in forgetin = { .nlookup = nlookup }; vu_devfuse_conversation(fusemount, FUSE_FORGET, nodeid, IOV1(&forgetin, sizeof(forgetin)), IOV_NOREPLY, NULL); } return 0; } int vu_fuse_rmdir(const char *pathname) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"RMDIR path:%s", pathname); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; uint64_t dirid; staterr = fuse_lstat(fusemount, pathname, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(pathname); if (basename == NULL) return errno = EINVAL, -1; int err = vu_devfuse_conversation(fusemount, FUSE_RMDIR, dirid, IOV1(basename, strlen(basename) + 1), IOV0, NULL); if (err < 0) return errno = -err, -1; uint64_t nlookup; if (fn_delnode(fusemount->fnbuf, nodeid, &nlookup) > 0) { struct fuse_forget_in forgetin = { .nlookup = nlookup }; vu_devfuse_conversation(fusemount, FUSE_FORGET, nodeid, IOV1(&forgetin, sizeof(forgetin)), IOV_NOREPLY, NULL); } return 0; } int vu_fuse_chmod(const char *pathname, mode_t mode, int fd, void *fdprivate) { (void) fd; (void) fdprivate; struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"CHMOD path:%s mode:%d", pathname, mode); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; struct fuse_setattr_in setattrin = { .valid = FATTR_MODE, .mode = (statbuf.st_mode & ~ALLPERMS) | (mode & ALLPERMS) }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_SETATTR, nodeid, IOV1(&setattrin, sizeof(setattrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); return 0; } int vu_fuse_lchown(const char *pathname, uid_t owner, gid_t group, int fd, void *fdprivate) { (void) fd; (void) fdprivate; struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"CHOWN path:%s uid:%d gid=%d", pathname, owner, group); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; struct fuse_setattr_in setattrin = { .valid = ((owner == ((uid_t) -1)) ? 0 : FATTR_UID) | ((group == ((uid_t) -1)) ? 0 : FATTR_GID), .uid = (owner == ((uid_t) -1)) ? 0 : owner, .gid = (group == ((gid_t) -1)) ? 0 : group }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_SETATTR, nodeid, IOV1(&setattrin, sizeof(setattrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); return 0; } int vu_fuse_utimensat(int dirfd, const char *pathname, \ const struct timespec times[2], int flags, int fd, void *fdprivate) { (void) dirfd; (void) flags; (void) fd; (void) fdprivate; struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"UTIMENSAT path:%s", pathname); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; struct fuse_setattr_in setattrin = { .valid = FATTR_ATIME | FATTR_MTIME, .atime = times[0].tv_sec, .atimensec = times[0].tv_nsec, .mtime = times[1].tv_sec, .mtimensec = times[1].tv_nsec }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_SETATTR, nodeid, IOV1(&setattrin, sizeof(setattrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); return 0; } int vu_fuse_truncate(const char *pathname, off_t length, int fd, void *fdprivate) { (void) fd; (void) fdprivate; struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"TRUNCATE path:%s %llu", pathname, length); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, pathname, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; struct fuse_setattr_in setattrin = { .valid = FATTR_SIZE, .size = length }; struct fuse_attr_out attrout; int err = vu_devfuse_conversation(fusemount, FUSE_SETATTR, nodeid, IOV1(&setattrin, sizeof(setattrin)), IOV1(&attrout, sizeof(attrout)), NULL); if (err < 0) return errno = -err, -1; fn_updatenode(fusemount->fnbuf, nodeid, &attrout); return 0; } int vu_fuse_symlink(const char *target, const char *linkpath) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"SYMLINK target: %s path:%s", target, linkpath); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t dirid; int staterr = fuse_lstat(fusemount, linkpath, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(linkpath); if (basename == NULL) return errno = EINVAL, -1; struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_SYMLINK, dirid, IOV2(basename, strlen(basename) + 1, target, strlen(target) + 1), IOV1(&entryout, sizeof(entryout)), NULL); if (err < 0) return errno = -err, -1; fn_add(fusemount->fnbuf, linkpath, &entryout); return 0; } int vu_fuse_link(const char *oldpath, const char *newpath) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"LINK oldpath: %s newpath:%s", oldpath, newpath); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, oldpath, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; uint64_t dirid; staterr = fuse_lstat(fusemount, newpath, &statbuf, &dirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *basename = fuse_basename(newpath); if (basename == NULL) return errno = EINVAL, -1; struct fuse_link_in linkin = { .oldnodeid = nodeid }; struct fuse_entry_out entryout; int err = vu_devfuse_conversation(fusemount, FUSE_LINK, dirid, IOV2(&linkin, sizeof(linkin), basename, strlen(basename) + 1), IOV1(&entryout, sizeof(entryout)), NULL); if (err < 0) return errno = -err, -1; fn_add(fusemount->fnbuf, newpath, &entryout); return 0; } int vu_fuse_rename(const char *oldpath, const char *newpath, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); if (ht == devfuse_ht) return vu_devfuse_nosys(); printkdebug(U,"RENAME oldpath: %s newpath:%s", oldpath, newpath); struct fusemount_t *fusemount = vuht_get_private_data(ht); if (fusemount->mountflags & MS_RDONLY) return errno = EROFS, -1; struct vu_stat statbuf; uint64_t nodeid; int staterr = fuse_lstat(fusemount, oldpath, &statbuf, &nodeid, LSTAT_THIS); if (staterr < 0) return -1; uint64_t olddirid; staterr = fuse_lstat(fusemount, oldpath, &statbuf, &olddirid, LSTAT_PARENT); if (staterr < 0) return -1; uint64_t newdirid; staterr = fuse_lstat(fusemount, newpath, &statbuf, &newdirid, LSTAT_PARENT); if (staterr < 0) return -1; const char *oldbasename = fuse_basename(oldpath); if (oldbasename == NULL) return errno = EINVAL, -1; const char *newbasename = fuse_basename(newpath); if (newbasename == NULL) return errno = EINVAL, -1; int err; if (fusemount->initdata.minor < 23) { struct fuse_rename_in renamein = { .newdir = newdirid }; err = vu_devfuse_conversation(fusemount, FUSE_RENAME, olddirid, IOV3(&renamein, sizeof(renamein), oldbasename, strlen(oldbasename) + 1, newbasename, strlen(newbasename) + 1), IOV0, NULL); } else { struct fuse_rename2_in renamein = { .newdir = newdirid, .flags = flags }; err = vu_devfuse_conversation(fusemount, FUSE_RENAME2, olddirid, IOV3(&renamein, sizeof(renamein), oldbasename, strlen(oldbasename) + 1, newbasename, strlen(newbasename) + 1), IOV0, NULL); } if (err < 0) return errno = -err, -1; uint64_t nlookup; if (fn_delnode(fusemount->fnbuf, nodeid, &nlookup) > 0) { struct fuse_forget_in forgetin = { .nlookup = nlookup }; vu_devfuse_conversation(fusemount, FUSE_FORGET, nodeid, IOV1(&forgetin, sizeof(forgetin)), IOV_NOREPLY, NULL); } return 0; } #if 0 int vu_fuse_ioctl(int fd, unsigned long request, void *buf, uintptr_t addr, void *fdprivate) { return vu_devfuse_nosys(); } #endif vuos-0.9.2/vufs/000077500000000000000000000000001476575172100135205ustar00rootroot00000000000000vuos-0.9.2/vufs/CMakeLists.txt000066400000000000000000000006761476575172100162710ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}) set(VU_MOD_TARGET vufs) file(GLOB_RECURSE VUFS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUFS_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} stropt volatilestream) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vufs/vufs.c000066400000000000000000000143261476575172100146550ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include static int vufs_confirm(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vufs_t *vufs = vuht_get_private_data(ht); char *shortpath = arg; char **exception; for (exception = vufs->except; *exception; exception++) { int len = strlen(*exception); if (strncmp(shortpath,*exception,len) == 0 && (shortpath[len] == '/' || shortpath[len]=='\0')) return 0; } return 1; } static int set_mount_options(const char *input, struct vufs_t *vufs) { int tagc = stropt(input, NULL, NULL, 0); int retval = 0; if(tagc > 1) { char buf[strlen(input)+1]; char *tags[tagc]; char *args[tagc]; int excl_choice = 0; stropt(input, tags, args, buf); for (int i=0; tags[i] != NULL; i++) { uint64_t strcasetag = strcase(tags[i]); if (vufs == NULL) { switch(strcasetag) { case STRCASE(e,x,c,e,p,t): retval++; if (args[i] == NULL) { printk(KERN_ERR "vufs: %s requires an arg\n", tags[i]); return -1; } break; case STRCASE(b,i,n,d): case STRCASE(m,o,v,e): case STRCASE(m,e,r,g,e): case STRCASE(c,o,w): case STRCASE(m,i,n,c,o,w): if (args[i] != NULL) { printk(KERN_ERR "vufs: %s need no args\n", tags[i]); return -1; } if (++excl_choice > 1) { printk(KERN_ERR "vufs: move, merge, cow and mincow are mutually exclusive\n", tags[i]); return -1; } break; case STRCASE(n,o,c,h,c,o,p,y): if (args[i] != NULL) { printk(KERN_ERR "vufs: %s need no args\n", tags[i]); return -1; } break; default: printk(KERN_ERR "vufs: %s unknown tag\n", tags[i]); return -1; break; } switch(strcasetag) { case STRCASE(e,x,c,e,p,t): retval++; break; } } else { switch(strcasetag) { case STRCASE(e,x,c,e,p,t): vufs->except[retval++] = strdup(args[i]); vufs->except[retval] = NULL; break; case STRCASE(m,o,v,e): case STRCASE(b,i,n,d): break; case STRCASE(m,e,r,g,e): vufs->flags |= VUFS_MERGE; break; case STRCASE(c,o,w): vufs->flags |= VUFS_COW; break; case STRCASE(m,i,n,c,o,w): vufs->flags |= VUFS_MINCOW; break; case STRCASE(n,o,c,h,c,o,p,y): vufs->flags |= VUFS_NOCHCOPY; break; } } } } return retval; } int vu_vufs_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { struct vu_service_t *s = vu_mod_getservice(); struct vufs_t *new_vufs; int nexcept; if (data == NULL) data = ""; if ((nexcept = set_mount_options(data, NULL)) < 0) { errno = EINVAL; return -1; } new_vufs = malloc(sizeof(struct vufs_t) + sizeof(char *) * (nexcept + 1)); if (new_vufs == NULL) { errno = ENOMEM; goto mallocerr; } new_vufs->source = strdup(source); new_vufs->target = strdup(target); new_vufs->except[0] = 0; new_vufs->rdirfd = -1; new_vufs->vdirfd = -1; new_vufs->ddirfd = -1; new_vufs->flags = 0; set_mount_options(data, new_vufs); new_vufs->vdirfd = open(source, O_PATH); if (new_vufs->vdirfd < 0) { errno = ENOENT; goto vdirerr; } if (new_vufs->flags & VUFS_TYPEMASK) { new_vufs->rdirfd = open(target, O_PATH); if (new_vufs->rdirfd < 0) { errno = ENOENT; goto rdirerr; } switch (new_vufs->flags & VUFS_TYPEMASK) { case VUFS_COW: case VUFS_MINCOW: mkdirat(new_vufs->vdirfd, ".-", 0777); } new_vufs->ddirfd = openat(new_vufs->vdirfd, ".-", O_PATH, 0777); } pthread_mutex_init(&(new_vufs->mutex), NULL); pthread_mutex_lock(&(new_vufs->mutex)); vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, vufs_confirm, new_vufs); pthread_mutex_unlock(&(new_vufs->mutex)); errno = 0; return 0; rdirerr: close(new_vufs->vdirfd); vdirerr: free(new_vufs); mallocerr: return -1; } int vu_vufs_umount2(const char *target, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); int ret_value; if ((ret_value = vuht_del(ht, flags)) < 0) { errno = -ret_value; return -1; } return 0; } void vu_vufs_cleanup(uint8_t type, void *arg, int arglen,struct vuht_entry_t *ht) { if (type == CHECKPATH) { struct vufs_t *vufs = vuht_get_private_data(ht); if (vufs == NULL) { errno = EINVAL; } else { if (vufs->ddirfd >= 0) close(vufs->ddirfd); if (vufs->rdirfd >= 0) close(vufs->rdirfd); close(vufs->vdirfd); pthread_mutex_destroy(&(vufs->mutex)); free(vufs->source); free(vufs->target); free(vufs); } } } void *vu_vufs_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, read) = read; vu_syscall_handler(s, write) = write; vu_syscall_handler(s, lseek) = lseek; vu_syscall_handler(s, pread64) = pread; vu_syscall_handler(s, pwrite64) = pwrite; vu_syscall_handler(s, fcntl) = fcntl; #pragma GCC diagnostic pop return NULL; } int vu_vufs_fini(void *private) { return 0; } struct vu_module_t vu_module = { .name = "vufs", .description = "vu filesystem patchworking" }; __attribute__((constructor)) static void init(void) { debug_set_name(V, "VUFS"); } __attribute__((destructor)) static void fini(void) { debug_set_name(V, ""); } vuos-0.9.2/vufs/vufs.h000066400000000000000000000007571476575172100146650ustar00rootroot00000000000000#ifndef VUFS_H #define VUFS_H #include #include VU_PROTOTYPES(vufs) #define O_UNLINK (O_PATH | O_EXCL) #define VUFS_TYPEMASK 0x7 #define VUFS_BIND 0x0 #define VUFS_MERGE 0x1 #define VUFS_COW 0x2 #define VUFS_MINCOW 0x4 #define VUFS_NOCHCOPY 0x100 struct vufs_t { pthread_mutex_t mutex; char *source; char *target; int rdirfd; int vdirfd; int ddirfd; int flags; char *except[]; }; struct vufs_fdprivate { FILE *getdentsf; char path[]; }; #endif vuos-0.9.2/vufs/vufs_getdents.c000066400000000000000000000165131476575172100165520ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* add an entry to the volatile stream for getdents */ static int vufs_filldir_entry(FILE *f, const char *name, unsigned char type, __ino64_t ino) { struct dirent64 entry = { .d_ino = ino, .d_type = type, .d_off = ftello(f), }; static char filler[7]; unsigned short int namelen = strlen(name) + 1; unsigned short int reclen = offsetof(struct dirent64, d_name) + namelen; int ret_value; snprintf(entry.d_name, 256, "%s", name); /* entries are always 8 bytes aligned */ entry.d_reclen = (reclen + 7) & (~7); ret_value = fwrite(&entry, reclen, 1, f); /* add a filler to align the next entry */ if (entry.d_reclen > reclen) ret_value += fwrite(filler, entry.d_reclen - reclen, 1, f); return 0; } /* check if a name is in the list of already seen names*/ /* the "list" is a concatenation of zero terminated strings. an empty entry is the tag of the end of list */ static int vufs_seen_entry(char *s, char *list) { char *scan = list; while (*scan) { if (strcmp(s, scan) == 0) return 1; scan += strlen(scan) + 1; } return 0; } static void vufs_filldir(unsigned int fd, struct vufs_t *vufs, struct vufs_fdprivate *vufs_fdprivate) { FILE *seenf = volstream_open(); void *seenlist = NULL; DIR *dir; struct dirent *de; int dirfd; vufs_fdprivate->getdentsf = volstream_open(); if (vufs_fdprivate->path[0] == 0) dirfd = openat(vufs->vdirfd, vufs->source, O_RDONLY | O_DIRECTORY); else dirfd = openat(vufs->vdirfd, vufs_fdprivate->path, O_RDONLY | O_DIRECTORY); if (dirfd) { dir = fdopendir(dirfd); if (dir) { /* ADD entries in vdirfd (source) */ while ((de = readdir(dir)) != NULL) { if (!(vufs_fdprivate->path[0] == 0 && strcmp(de->d_name, ".-") == 0)) { vufs_filldir_entry(vufs_fdprivate->getdentsf, de->d_name, de->d_type, de->d_ino); if (vufs->rdirfd >= 0) fwrite(de->d_name, strlen(de->d_name) + 1, 1, seenf); } } closedir(dir); } } if (vufs->rdirfd >= 0) { /* ADD deleted entries (ddirfd) in seenlist (if merge) */ if (vufs->ddirfd >= 0) { if (vufs_fdprivate->path[0] == 0) dirfd = openat(vufs->vdirfd, ".-", O_RDONLY | O_DIRECTORY); else dirfd = openat(vufs->ddirfd, vufs_fdprivate->path, O_RDONLY | O_DIRECTORY); if (dirfd >= 0) { dir = fdopendir(dirfd); while ((de = readdir(dir)) != NULL) { struct vu_stat buf; if (fstatat(dirfd, de->d_name, &buf, 0) == 0 && S_ISREG(buf.st_mode)) fwrite(de->d_name, strlen(de->d_name) + 1, 1, seenf); } closedir(dir); } } /* write the empty string as the end of the seen list */ fwrite("", 1, 1, seenf); fflush(seenf); volstream_getbuf(seenf, &seenlist, NULL); /* ADD unseen entries in rdirfd (target) (if merge) */ if (vufs_fdprivate->path[0] == 0) dirfd = openat(vufs->rdirfd, vufs->target, O_RDONLY | O_DIRECTORY); else dirfd = openat(vufs->rdirfd, vufs_fdprivate->path, O_RDONLY | O_DIRECTORY); if (dirfd >= 0) { dir = fdopendir(dirfd); while ((de = readdir(dir)) != NULL) { if (! vufs_seen_entry(de->d_name, seenlist)) vufs_filldir_entry(vufs_fdprivate->getdentsf, de->d_name, de->d_type, de->d_ino); } closedir(dir); } } fclose(seenf); fseeko(vufs_fdprivate->getdentsf, 0, SEEK_SET); } int vu_vufs_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *fdprivate) { struct vufs_t *vufs = vu_get_ht_private_data(); int vufs_type = vufs->flags & VUFS_TYPEMASK; if (vufs_type == VUFS_BIND) return syscall(__NR_getdents64, fd, dirp, count); else if (fdprivate != NULL) { int retval = 0; pthread_mutex_lock(&(vufs->mutex)); struct vufs_fdprivate *vufs_fdprivate = fdprivate; if (vufs_fdprivate->getdentsf == NULL) vufs_filldir(fd, vufs, vufs_fdprivate); if (vufs_fdprivate->getdentsf != NULL) { retval = fread(dirp, 1, count, vufs_fdprivate->getdentsf); if (retval == (int) count) { unsigned int bpos = 0; struct dirent64 *d; char *buf = (char *) dirp; while (1) { d = (struct dirent64 *) (buf + bpos); if (count - bpos < offsetof(struct dirent64, d_name)) break; if (bpos + d->d_reclen > count) break; bpos += d->d_reclen; } if (bpos < count) { fseeko(vufs_fdprivate->getdentsf, - (int) (count - bpos), SEEK_CUR); retval -= count - bpos; } /* the buffer is so short that it does not fit one entry. Return EINVAL! */ if (retval == 0) { errno = EINVAL; retval = -1; } } } pthread_mutex_unlock(&(vufs->mutex)); printkdebug(V, "GETDENTS retvalue:%d", retval); return retval; } else { errno = EBADF; return -1; } } static int skipdir(const char *name, int dotdelete) { if (name[0] == 0 || name[0] != '.') return 0; if (name[1] == 0) return 1; if (name[1] == '.' && name[2] == 0) return 1; if (dotdelete && name[1] == '-' && name[2] == 0) return 1; return 0; } static int vufs_enotempty_dir(int fd, int dfd, int dotdelete) { DIR *dir; struct dirent *de; int retval = 0; int errno_copy; dir = fdopendir(fd); while ((de = readdir(dir)) != NULL) { if (skipdir(de->d_name, dotdelete) == 0) { struct vu_stat buf; if (dfd < 0 || fstatat(dfd, de->d_name, &buf, 0) < 0 || !S_ISREG(buf.st_mode)) { retval = -1; errno = ENOTEMPTY; break; } } } errno_copy = errno; closedir(dir); if (dfd >= 0) close(dfd); errno = errno_copy; return retval; } int vufs_enotempty_ck(struct vufs_t *vufs, const char *path) { int vfd = openat(vufs->vdirfd, *path == 0 ? vufs->source : path, O_RDONLY | O_DIRECTORY); int rfd; int dfd; /* scan virt dir, if there is at least a file, dir is not empty */ if (vfd >= 0) { if (vufs_enotempty_dir(vfd, -1, *path == 0) < 0) return -1; } else if (errno != ENOENT) return -1; /* the virt dir either does not exist or it is empty */ /* try the real directory */ /* let us first test if the dir has been virtually deleted */ if (*path == 0) dfd = openat(vufs->vdirfd, ".-", O_PATH); else dfd = openat(vufs->ddirfd, path, O_PATH); if (dfd > 0) { struct vu_stat buf; if (fstatat(dfd, "", &buf, AT_EMPTY_PATH) == 0 && S_ISREG(buf.st_mode)) { close(dfd); return vfd >= 0 ? 0 : -1; } } /* scan the real dir looking for (undeleted) files */ rfd = openat(vufs->rdirfd, *path == 0 ? vufs->target : path, O_RDONLY | O_DIRECTORY); if (rfd >= 0) return vufs_enotempty_dir(rfd, dfd, 0); else { if (dfd >= 0) close(dfd); return vfd >= 0 ? 0 : -1; } } vuos-0.9.2/vufs/vufs_getdents.h000066400000000000000000000002071476575172100165500ustar00rootroot00000000000000#ifndef VUFS_GETDENTS_H #define VUFS_GETDENTS_H struct vufs_t; int vufs_enotempty_ck(struct vufs_t *vufs, const char *path); #endif vuos-0.9.2/vufs/vufs_path.c000066400000000000000000000055111476575172100156650ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include void vufs_create_path(int dirfd, const char *path, create_path_cb_t callback, void *arg) { int pathlen = strlen(path); char tpath[pathlen]; int i; for (i = 0; i < pathlen; i++) { if (path[i] == '/') { tpath[i] = 0; if (mkdirat(dirfd, tpath, 0700) == 0 && callback) callback(arg, dirfd, tpath); } tpath[i] = path[i]; } } void vufs_destroy_path(int dirfd, const char *path) { int pathlen = strlen(path); char tpath[pathlen + 1]; int i; strcpy(tpath, path); for (i = pathlen - 1; i >= 0; i--) { if (tpath[i] == '/') { tpath[i] = 0; if (unlinkat(dirfd, tpath, AT_REMOVEDIR) < 0) break; } } } static int skipdir(const char *name) { if (name[0] == 0 || name[0] != '.') return 0; if (name[1] == 0) return 1; if (name[1] == '.' && name[2] == 0) return 1; return 0; } void vufs_destroy_tree(int dirfd, const char *path, int recursive) { int retval = unlinkat(dirfd, path, 0); if (retval < 0 && errno == EISDIR) { retval = unlinkat(dirfd, path, AT_REMOVEDIR); if (retval < 0 && (errno == ENOTEMPTY || errno == EEXIST)) { int fd = openat(dirfd, path, O_RDONLY | O_DIRECTORY); if (fd >= 0) { DIR *dir; struct dirent *de; dir = fdopendir(fd); if (dir) { while ((de = readdir(dir)) != NULL) { if (skipdir(de->d_name) == 0) { if (recursive) vufs_destroy_tree(fd, de->d_name, recursive); else unlinkat(fd, de->d_name, 0); } } closedir(dir); } } unlinkat(dirfd, path, AT_REMOVEDIR); } } } int vufs_whiteout(int dirfd, const char *path) { if (dirfd >= 0) { vufs_destroy_tree(dirfd, path, 0); vufs_create_path(dirfd, path, NULL, NULL); return mknodat(dirfd, path, S_IFREG | 0644, 0); } else return 0; } void vufs_dewhiteout(int dirfd, const char *path) { if (dirfd >= 0) { if (unlinkat(dirfd, path, 0) == 0) vufs_destroy_path(dirfd, path); } } vuos-0.9.2/vufs/vufs_path.h000066400000000000000000000006621476575172100156740ustar00rootroot00000000000000#ifndef VUFS_PATH_H #define VUFS_PATH_H typedef void (*create_path_cb_t)(void *arg, int dirfd, const char *path); void vufs_create_path(int dirfd, const char *path, create_path_cb_t callback, void *arg); void vufs_destroy_path(int dirfd, const char *path); void vufs_destroy_tree(int dirfd, const char *path, int recursive); int vufs_whiteout(int dirfd, const char *path); void vufs_dewhiteout(int dirfd, const char *path); #endif vuos-0.9.2/vufs/vufsa.c000066400000000000000000000221331476575172100150110ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include static inline void vufs_lock(struct vufs_t *vufs) { pthread_mutex_lock(&(vufs->mutex)); } static inline void vufs_unlock(struct vufs_t *vufs) { pthread_mutex_unlock(&(vufs->mutex)); } static inline int o_is_creat_excl(int flags) { return (flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL); } static inline int o_is_unlink(int flags) { return flags == O_UNLINK; } static int vufs_vdeleted(struct vufs_t *vufs, const char *path) { struct vu_stat buf; if (vufs->ddirfd >= 0) { if (fstatat(vufs->ddirfd, path, &buf, AT_EMPTY_PATH) == 0) return S_ISREG(buf.st_mode); else return errno == ENOTDIR; // a component in the path has been deleted } else return 0; } static inline int vufs_vexist (struct vufs_t *vufs, const char *path, int flags) { struct vu_stat buf; if (fstatat(vufs->vdirfd, path, &buf, flags | AT_EMPTY_PATH) == 0) return 1; else if (errno == ENOENT) return 0; else return 1; } static inline int vufs_rexist (struct vufs_t *vufs, const char *path, int flags) { struct vu_stat buf; if (fstatat(vufs->rdirfd, path, &buf, flags | AT_EMPTY_PATH) == 0) return 1; else if (errno == ENOENT) return 0; else return 1; } static vufsa_status vufsa_rdonly(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); return VUFSA_DOVIRT; case VUFSA_DOVIRT: if (rv < 0 && errno == ENOENT) { if (vufs_vdeleted(vufs, path)) { errno = ENOENT; return VUFSA_FINAL; } else return VUFSA_DOREAL; } else return VUFSA_FINAL; case VUFSA_DOREAL: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_bind(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: return VUFSA_DOVIRT; case VUFSA_DOVIRT: case VUFSA_ERR: break; default: return VUFSA_ERR; } return VUFSA_EXIT; } static vufsa_status vufsa_merge(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, 0) || vufs_vdeleted(vufs, path)) { errno = EROFS; return VUFSA_ERR; } else return VUFSA_DOREAL; case VUFSA_DOREAL: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_merge_unlink(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, AT_SYMLINK_NOFOLLOW)) { errno = EROFS; return VUFSA_ERR; } else if (vufs_vdeleted(vufs, path)) { errno = ENOENT; return VUFSA_ERR; } else return VUFSA_DOREAL; case VUFSA_DOREAL: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_cow(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, 0) || vufs_vdeleted(vufs, path)) return VUFSA_DOVIRT; else return VUFSA_DOCOPY; case VUFSA_DOCOPY: return VUFSA_DOVIRT; case VUFSA_DOVIRT: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_cow_creat(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_rexist(vufs, path, 0) && !vufs_vdeleted(vufs, path)) { errno = EEXIST; return VUFSA_ERR; } else return VUFSA_DOVIRT; case VUFSA_DOVIRT: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_cow_unlink(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, AT_SYMLINK_NOFOLLOW)) return VUFSA_DOVIRT; else if (vufs_vdeleted(vufs, path)) { errno = ENOENT; return VUFSA_ERR; } else if (vufs_rexist(vufs, path, AT_SYMLINK_NOFOLLOW)) return VUFSA_VUNLINK; else { errno = ENOENT; return VUFSA_ERR; } case VUFSA_DOVIRT: if (rv == 0 && vufs_rexist(vufs, path, 0) && !vufs_vdeleted(vufs, path)) return VUFSA_VUNLINK; else return VUFSA_FINAL; case VUFSA_VUNLINK: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_mincow(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, 0) || vufs_vdeleted(vufs, path)) return VUFSA_DOVIRT; else return VUFSA_DOREAL; case VUFSA_DOREAL: if (rv < 0 && (errno == EACCES || errno == EPERM)) return VUFSA_DOCOPY; else return VUFSA_FINAL; case VUFSA_DOCOPY: return VUFSA_DOVIRT; case VUFSA_DOVIRT: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_mincow_creat(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_rexist(vufs, path, 0)) { if (vufs_vdeleted(vufs, path)) return VUFSA_DOVIRT; else { errno = EEXIST; return VUFSA_ERR; } } else return VUFSA_DOREAL; case VUFSA_DOREAL: if (rv < 0 && (errno == EACCES || errno == ENOENT || errno == EPERM)) return VUFSA_DOVIRT; else return VUFSA_FINAL; case VUFSA_DOVIRT: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_mincow_unlink(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_START: vufs_lock(vufs); if (vufs_vexist(vufs, path, AT_SYMLINK_NOFOLLOW)) return VUFSA_DOVIRT; else if (vufs_vdeleted(vufs, path)) { errno = ENOENT; return VUFSA_ERR; } else if (vufs_rexist(vufs, path, AT_SYMLINK_NOFOLLOW)) return VUFSA_DOREAL; else { errno = ENOENT; return VUFSA_ERR; } case VUFSA_DOREAL: if (rv < 0 && (errno == EACCES || errno == EPERM)) return VUFSA_VUNLINK; else return VUFSA_FINAL; case VUFSA_DOVIRT: if (rv == 0 && vufs_rexist(vufs, path, AT_SYMLINK_NOFOLLOW) && !vufs_vdeleted(vufs, path)) return VUFSA_VUNLINK; else return VUFSA_FINAL; case VUFSA_VUNLINK: return VUFSA_FINAL; case VUFSA_FINAL: case VUFSA_ERR: break; default: return VUFSA_ERR; } vufs_unlock(vufs); return VUFSA_EXIT; } static vufsa_status vufsa_err(vufsa_status status, struct vufs_t *vufs, const char *path, int rv) { switch (status) { case VUFSA_ERR: break; default: return VUFSA_ERR; }; return VUFSA_EXIT; } vufsa_next vufsa_select(struct vufs_t *vufs, int open_flags) { int vufs_type = vufs->flags & VUFS_TYPEMASK; if (vufs_type == VUFS_BIND) return vufsa_bind; if (vufs_type == VUFS_MERGE) { if (o_is_creat_excl(open_flags)) return vufsa_merge; else if (o_is_unlink(open_flags)) return vufsa_merge_unlink; else if ((open_flags & O_ACCMODE) == O_RDONLY) return vufsa_rdonly; else return vufsa_merge; } else if (vufs_type == VUFS_COW) { if (o_is_creat_excl(open_flags)) return vufsa_cow_creat; else if (o_is_unlink(open_flags)) return vufsa_cow_unlink; else if ((open_flags & O_ACCMODE) == O_RDONLY) return vufsa_rdonly; else return vufsa_cow; } else if (vufs_type == VUFS_MINCOW) { if (o_is_creat_excl(open_flags)) return vufsa_mincow_creat; else if (o_is_unlink(open_flags)) return vufsa_mincow_unlink; else if ((open_flags & O_ACCMODE) == O_RDONLY) return vufsa_rdonly; else return vufsa_mincow; } return vufsa_err; } vuos-0.9.2/vufs/vufsa.h000066400000000000000000000020651476575172100150200ustar00rootroot00000000000000#ifndef VUFSA_H #define VUFSA_H struct vufs_t; typedef int8_t vufsa_status; /* type of FSA next function */ typedef vufsa_status (*vufsa_next)(vufsa_status status, struct vufs_t *vufs, const char *path, int rv); /* select the right FSA depending upon the mount mode and open flags */ vufsa_next vufsa_select(struct vufs_t *vufs, int open_flags); #define VUFSA_ERR -1 #define VUFSA_EXIT 0 #define VUFSA_START 1 #define VUFSA_FINAL 2 #define VUFSA_DOREAL 3 #define VUFSA_DOVIRT 4 #define VUFSA_DOCOPY 5 #define VUFSA_VUNLINK 5 #if 0 /* usage: */ int rv; vufsa_status status = VUFSA_START; vufsa_next vufsa_next = vufsa_select(vufs, openflag); while ((status = vufsa_next(status, vufs, path, openflag, 0, rv)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: rv = /* implementation of do_real for this syscall */ break; case VUFSA_DOVIRT: rv = /* implementation of do_virtual for this syscall */ break; case VUFSA_DOCOPY: rv = /* implementation of do_copy for this syscall */ break; case VUFSA_ERR: rv = -1; break; } #endif #endif vuos-0.9.2/vufs/vufsops.c000066400000000000000000000573011476575172100153770ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAXSIZE ((1ULL<<((sizeof(size_t)*8)-1))-1) #define CHUNKSIZE 4096 #define log_if_err(S, X) \ do { \ if ((X) < 0) \ printkdebug(V, "ERR: " #S "errno:%d", errno); \ } while (0) static void vufs_copyfile_stat(struct vufs_t *vufs, const char *path, struct vu_stat *rstat, struct vu_stat *vstat) { uid_t newuid = -1; gid_t newgid = -1; if (((rstat->st_mode ^ vstat->st_mode) & ~S_IFMT) != 0) { if (fchmodat(vufs->vdirfd, path, rstat->st_mode & ~S_IFMT, AT_SYMLINK_NOFOLLOW) < 0) { if (((rstat->st_mode ^ vstat->st_mode) & 0777) != 0) log_if_err(fchmodat, fchmodat(vufs->vdirfd, path, rstat->st_mode & 0777, AT_SYMLINK_NOFOLLOW)); } } if (rstat->st_uid != vstat->st_uid) newuid = rstat->st_uid; if (rstat->st_gid != vstat->st_gid) newgid = rstat->st_gid; if (newuid != (uid_t) -1 || newgid != (gid_t) -1) log_if_err(fchownat, fchownat(vufs->vdirfd, path, newuid, newgid, AT_SYMLINK_NOFOLLOW)); } static void vufs_copyfile_vufstat(struct vufs_t *vufs, const char *path, struct vu_stat *rstat, struct vu_stat *vstat) { uint32_t mask = vufstat_cmpstat(rstat, vstat) & VUFSTAT_COPYMASK; vufstat_write(vufs->ddirfd, path, rstat, mask); } static void vufs_copyfile_create_path_cb(void *arg, int dirfd, const char *path) { struct vufs_t *vufs = arg; struct vu_stat rstat; struct vu_stat vstat; struct vu_stat newvstat; if (fstatat(vufs->vdirfd, path, &vstat, AT_EMPTY_PATH) == 0 && fstatat(vufs->rdirfd, path, &rstat, AT_EMPTY_PATH) == 0) { vufstat_merge(vufs->ddirfd, path, &rstat); vufs_copyfile_stat(vufs, path, &rstat, &vstat); if (fstatat(vufs->vdirfd, path, &newvstat, AT_EMPTY_PATH) == 0) { uint32_t mask = vufstat_cmpstat(&rstat, &newvstat) & VUFSTAT_COPYMASK; vufstat_write(vufs->ddirfd, path, &rstat, mask); } } } static int vufs_copyfile(struct vufs_t *vufs, const char *path, size_t truncate) { int fdin = openat(vufs->rdirfd, path, O_RDONLY, 0); if (fdin >= 0) { struct vu_stat instat; fstat(fdin, &instat); if (!S_ISREG(instat.st_mode)) { errno = -EIO; close(fdin); return -1; } else { int fdout; vufs_create_path(vufs->vdirfd, path, vufs_copyfile_create_path_cb, vufs); fdout = openat(vufs->vdirfd, path, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fdout >= 0) { struct vu_stat outstat; size_t nread, readsize = CHUNKSIZE; char buf[CHUNKSIZE]; fstat(fdout, &outstat); while (1) { if (truncate < readsize) readsize = truncate; nread = read(fdin, buf, readsize); if (nread <= 0) break; truncate -= nread; nread = write(fdout, buf, nread); if (nread <= 0) break; } vufstat_merge(vufs->ddirfd, path, &instat); vufs_copyfile_stat(vufs, path, &instat, &outstat); fstat(fdout, &outstat); vufs_copyfile_vufstat(vufs, path, &instat, &outstat); close(fdin); close(fdout); return nread == 0 ? 0 : -1; } else { close(fdout); errno = EIO; return -1; } } } else { close(fdin); return -1; } } static void vufs_newopenfilestat(struct vufs_t *vufs, const char *path, int fd, mode_t mode) { struct vu_stat vstat; struct vu_stat newvstat; uint32_t mask; newvstat.st_uid = setfsuid(-1); newvstat.st_gid = setfsgid(-1); // XXX TBD setgid bit on dir newvstat.st_mode = mode & ~vu_mod_getumask(); log_if_err(fchown, fchown(fd, newvstat.st_uid, newvstat.st_gid)); if (fchmod(fd, newvstat.st_mode & ~S_IFMT) < 0) fchmod(fd, newvstat.st_mode & 0777); fstat(fd, &vstat); mask = vufstat_cmpstat(&vstat, &newvstat) & VUFSTAT_COPYMASK; vufstat_write(vufs->ddirfd, path, &newvstat, mask); } static void vufs_newfilestat(struct vufs_t *vufs, const char *path, struct vu_stat *newvstat, uint32_t mask, mode_t mode) { struct vu_stat vstat; if ((mask & VUFSTAT_UID) == 0) newvstat->st_uid = setfsuid(-1); if ((mask & VUFSTAT_GID) == 0) newvstat->st_gid = setfsgid(-1); // XXX TBD setgid bit on dir if ((mask & VUFSTAT_MODE) == 0) newvstat->st_mode = mode & ~vu_mod_getumask(); log_if_err(fchownat, fchownat(vufs->vdirfd, path, newvstat->st_uid, newvstat->st_gid, AT_EMPTY_PATH)); if (fchmodat(vufs->vdirfd, path, newvstat->st_mode & ~S_IFMT, AT_EMPTY_PATH) < 0) log_if_err(fchmodat, fchmodat(vufs->vdirfd, path, newvstat->st_mode & 0777, AT_EMPTY_PATH)); fstatat(vufs->vdirfd, path, &vstat, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); mask |= vufstat_cmpstat(&vstat, newvstat) & VUFSTAT_COPYMASK; vufstat_write(vufs->ddirfd, path, newvstat, mask); } /* RDONLY SYSCALLS */ int vu_vufs_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate) { struct vufs_t *vufs = vu_get_ht_private_data(); pathname += 1; if (sfd >= 0) { int retval = fstat(sfd, buf); if (retval == 0) vufstat_merge(vufs->ddirfd, pathname, buf); return retval; } else { int retval = 0; vufsa_status status = VUFSA_START; vufsa_next vufsa_next = vufsa_select(vufs, O_RDONLY); while ((status = vufsa_next(status, vufs, pathname, retval)) != VUFSA_EXIT) { //printk("LSTAT status %d %d %d\n", status,retval, errno); switch (status) { case VUFSA_DOREAL: retval = fstatat(vufs->rdirfd, pathname, buf, flags | AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = fstatat(vufs->vdirfd, pathname, buf, flags | AT_EMPTY_PATH); break; case VUFSA_FINAL: if (retval == 0) vufstat_merge(vufs->ddirfd, pathname, buf); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "LSTAT path:%s retvalue:%d errno:%d", pathname, retval, retval < 0 ? errno : 0); return retval; } } int vu_vufs_access(char *path, int mode, int flags) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDONLY); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = faccessat(vufs->rdirfd, path, mode, flags | AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = faccessat(vufs->vdirfd, path, mode, flags | AT_EMPTY_PATH); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "ACCESS path:%s mode:%o retvalue:%d", path, mode, retval); return retval; } ssize_t vu_vufs_readlink(char *path, char *buf, size_t bufsiz) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDONLY); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = readlinkat(vufs->rdirfd, path, buf, bufsiz); break; case VUFSA_DOVIRT: retval = readlinkat(vufs->vdirfd, path, buf, bufsiz); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "READLINK path:%s retvalue:%d", path, retval); return retval; } int vu_vufs_statfs (const char *path, struct statfs *buf, int sfd, void *fdprivate) { if (sfd >= 0) { return fstatfs(sfd, buf); } else { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDONLY); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: /* openat O_EMPTY_PATH needed here */ if (*path == 0) sfd = dup(vufs->rdirfd); else sfd = openat(vufs->rdirfd, path, O_PATH); if (sfd < 0) retval = -1; else { retval = fstatfs(sfd, buf); close(sfd); } break; case VUFSA_DOVIRT: /* openat O_EMPTY_PATH needed here */ if (*path == 0) sfd = dup(vufs->vdirfd); else sfd = openat(vufs->vdirfd, path, O_PATH); if (sfd < 0) retval = -1; else { retval = fstatfs(sfd, buf); close(sfd); } break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "STATFS path:%s retvalue:%d", path, retval); return retval; } } /* ALWAYS DELETE SYSCALLS */ int vu_vufs_unlink (const char *path) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_UNLINK); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = unlinkat(vufs->rdirfd, path, 0); break; case VUFSA_DOVIRT: retval = unlinkat(vufs->vdirfd, path, 0); break; case VUFSA_VUNLINK: retval = vufs_whiteout(vufs->ddirfd, path); break; case VUFSA_FINAL: if (vufs->ddirfd >= 0) vufstat_unlink(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "UNLINK path:%s retvalue:%d", path, retval); return retval; } int vu_vufs_rmdir(const char *path) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; if (vufs_enotempty_ck(vufs, path) < 0) retval = -1; else { vufsa_next vufsa_next = vufsa_select(vufs, O_UNLINK); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = unlinkat(vufs->rdirfd, path, AT_REMOVEDIR); break; case VUFSA_DOVIRT: retval = unlinkat(vufs->vdirfd, path, AT_REMOVEDIR); break; case VUFSA_VUNLINK: retval = vufs_whiteout(vufs->ddirfd, path); break; case VUFSA_FINAL: if (vufs->ddirfd >= 0) vufstat_unlink(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } } printkdebug(V, "RMDIR path:%s retvalue:%d", path, retval); return retval; } /* ALWAYS CREATE SYSCALLS */ int vu_vufs_mkdir (const char *path, mode_t mode) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_CREAT | O_EXCL); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = mkdirat(vufs->rdirfd, path, mode); break; case VUFSA_DOVIRT: retval = mkdirat(vufs->vdirfd, path, mode); if (retval >= 0 && vufs->ddirfd >= 0) { struct vu_stat statbuf; vufs_newfilestat(vufs, path, &statbuf, 0, mode); } break; case VUFSA_FINAL: // now virt file exists, no need for whiteout file if (retval >=0) vufs_dewhiteout(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "MKDIR path:%s mode:%o retvalue:%d", path, mode, retval); return retval; } int vu_vufs_symlink (const char *target, const char *path) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_CREAT | O_EXCL); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = symlinkat(target, vufs->rdirfd, path); break; case VUFSA_DOVIRT: retval = symlinkat(target, vufs->vdirfd, path); if (retval >= 0 && vufs->ddirfd >= 0) { struct vu_stat statbuf; statbuf.st_mode = 0777; vufs_newfilestat(vufs, path, &statbuf, VUFSTAT_MODE, 0); } break; case VUFSA_FINAL: // now virt file exists, no need for whiteout file if (retval >=0) vufs_dewhiteout(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "SYMLINK path:%s target:%s retvalue:%d", path, target, retval); return retval; } int vu_vufs_mknod (const char *path, mode_t mode, dev_t dev) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_CREAT | O_EXCL); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = mknodat(vufs->rdirfd, path, mode, dev); break; case VUFSA_DOVIRT: retval = mknodat(vufs->vdirfd, path, mode, dev); if (retval < 0 && vufs->ddirfd >= 0) retval = mknodat(vufs->vdirfd, path, (mode & ~S_IFMT) | S_IFREG, 0); if (retval >= 0 && vufs->ddirfd >= 0) { struct vu_stat buf; buf.st_mode = mode; buf.st_rdev = dev; vufs_newfilestat(vufs, path, &buf, VUFSTAT_TYPE | VUFSTAT_RDEV, mode); } break; case VUFSA_FINAL: // now virt file exists, no need for whiteout file if (retval >=0) vufs_dewhiteout(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "MKNOD path:%s mode:%o major:%d minor:%d retval:%d", path, mode, major(dev), minor(dev), retval); return retval; } /* LINK - RENAME */ int vu_vufs_link (const char *oldpath, const char *newpath) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; newpath += 1; oldpath += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_CREAT | O_EXCL); while ((status = vufsa_next(status, vufs, newpath, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = linkat(vufs->rdirfd, oldpath, vufs->rdirfd, newpath, 0); break; case VUFSA_DOVIRT: retval = linkat(vufs->vdirfd, oldpath, vufs->vdirfd, newpath, 0); if (vufs->rdirfd >= 0) { if (retval < 0 && errno == ENOENT) { retval = vufs_copyfile(vufs, oldpath, MAXSIZE); if (retval == 0) { retval = linkat(vufs->vdirfd, oldpath, vufs->vdirfd, newpath, 0); if (retval < 0) { unlinkat(vufs->vdirfd, oldpath, 0); vufstat_unlink(vufs->ddirfd, oldpath); } } if (retval == 0) vufstat_link(vufs->ddirfd, oldpath, newpath); } } break; case VUFSA_FINAL: // now virt file exists, no need for whiteout file if (retval >=0) vufs_dewhiteout(vufs->ddirfd, newpath); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "LINK oldpath:%s newpath:%s retvalue:%d", oldpath, newpath, retval); return retval; } int vu_vufs_rename (const char *oldpath, const char *newpath, int flags) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; newpath += 1; oldpath += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDWR); while ((status = vufsa_next(status, vufs, newpath, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = syscall(__NR_renameat2, vufs->rdirfd, oldpath, vufs->rdirfd, newpath, flags); break; case VUFSA_DOVIRT: retval = syscall(__NR_renameat2, vufs->vdirfd, oldpath, vufs->vdirfd, newpath, flags); if (vufs->rdirfd >= 0) { if (retval < 0 && errno == ENOENT && vufs->rdirfd >= 0) { retval = vufs_copyfile(vufs, oldpath, MAXSIZE); if (retval == 0) { retval = syscall(__NR_renameat2, vufs->vdirfd, oldpath, vufs->vdirfd, newpath, flags); if (retval < 0) { unlinkat(vufs->vdirfd, oldpath, 0); vufstat_unlink(vufs->ddirfd, oldpath); } else vufs_whiteout(vufs->ddirfd, oldpath); } } if (retval >= 0) vufstat_rename(vufs->ddirfd, oldpath, newpath, flags); } break; case VUFSA_FINAL: // now virt file exists, no need for whiteout file if (retval >=0) vufs_dewhiteout(vufs->ddirfd, newpath); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "RENAME oldpath:%s newpath:%s retvalue:%d", oldpath, newpath, retval); return retval; //return syscall(__NR_renameat2, vufs->vdirfd, newpath + 1, vufs->vdirfd, oldpath + 1, flags); } /* TRUNCATE always modifies never creates */ /* for an unknown reason truncateat is missing */ static int ftruncateat(int dirfd, const char *pathname, off_t length, int flags) { if (flags & ~AT_EMPTY_PATH) return errno = -EINVAL, -1; if (flags & AT_EMPTY_PATH && *pathname == 0) return ftruncate(dirfd, length); else { int fd = openat(dirfd, pathname, O_WRONLY); if (fd < 0) return fd; int rv = ftruncate(fd, length); close(fd); return rv; } } int vu_vufs_truncate(const char *path, off_t length, int sfd, void *fdprivate) { if (sfd >= 0) { return ftruncate(sfd, length); } else { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDWR); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = ftruncateat(vufs->rdirfd, path, length, AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = ftruncateat(vufs->vdirfd, path, length, AT_EMPTY_PATH); break; case VUFSA_DOCOPY: retval = vufs_copyfile(vufs, path, length); // now virt file exists, no need for whiteout file // maybe useless? if (retval >=0) vufs_dewhiteout(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "TRUNCATE path:%s len:%zd retvalue:%d", path, length, retval); return retval; } } int vu_vufs_utimensat (int dirfd, const char *path, const struct timespec times[2], int flags, int sfd, void *fdprivate) { if (sfd >= 0) { return futimens(sfd, times); } else { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDWR); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = utimensat(vufs->rdirfd, path, times, flags | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = utimensat(vufs->vdirfd, path, times, flags | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); break; case VUFSA_DOCOPY: retval = vufs_copyfile(vufs, path, MAXSIZE); // now virt file exists, no need for whiteout file // maybe useless? if (retval >=0) vufs_dewhiteout(vufs->ddirfd, path); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "UTIMENSAT path:%s retvalue:%d", path, retval); return retval; } } /* MODIFY STAT SYSCALLS */ int vu_vufs_lchown(const char *path, uid_t owner, gid_t group, int fd, void *fdprivate) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDWR); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = fchownat(vufs->rdirfd, path, owner, group, AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = fchownat(vufs->vdirfd, path, owner, group, AT_EMPTY_PATH); if (vufs->vdirfd >= 0) { struct vu_stat statbuf = {.st_uid = owner, .st_gid = group}; uint32_t mask = 0; if (owner != (uid_t) -1) mask |= VUFSTAT_UID; if (group != (gid_t) -1) mask |= VUFSTAT_GID; vufstat_update(vufs->ddirfd, path, &statbuf, mask, retval < 0 && (errno == EPERM || errno == ENOENT) ? O_CREAT : 0); retval = 0; } break; case VUFSA_DOCOPY: if ((vufs->flags & VUFS_NOCHCOPY) == 0) retval = vufs_copyfile(vufs, path, MAXSIZE); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "CHOWN path:%s uid:%d gid:%d retvalue:%d", path, owner, group, retval); return retval; } int vu_vufs_chmod(const char *path, mode_t mode, int fd, void *fdprivate) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; path += 1; vufsa_next vufsa_next = vufsa_select(vufs, O_RDWR); while ((status = vufsa_next(status, vufs, path, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: retval = fchmodat(vufs->rdirfd, path, mode, AT_EMPTY_PATH); break; case VUFSA_DOVIRT: retval = fchmodat(vufs->vdirfd, path, mode, AT_EMPTY_PATH); if (vufs->vdirfd >= 0) { struct vu_stat statbuf = {.st_mode = mode}; vufstat_update(vufs->ddirfd, path, &statbuf, VUFSTAT_MODE, retval < 0 && (errno == EPERM || errno == ENOENT) ? O_CREAT : 0); retval = 0; } break; case VUFSA_DOCOPY: if ((vufs->flags & VUFS_NOCHCOPY) == 0) retval = vufs_copyfile(vufs, path, MAXSIZE); break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "CHMOD path:%s uid:0%o retvalue:%d", path, mode, retval); return retval; } /* OPEN (can be RDONLY, RDWR CREATE!) */ int vu_vufs_open(const char *pathname, int flags, mode_t mode, void **private) { struct vufs_t *vufs = vu_get_ht_private_data(); vufsa_status status = VUFSA_START; int retval = 0; const char *filepath; mode_t oldmode = vu_mod_getmode(); pathname += 1; if (flags == O_UNLINK) flags = O_PATH; //PATH+EXCL has the special meaning of UNLINK vufsa_next vufsa_next = vufsa_select(vufs, flags); while ((status = vufsa_next(status, vufs, pathname, retval)) != VUFSA_EXIT) { switch (status) { case VUFSA_DOREAL: /* openat O_EMPTY_PATH needed here */ filepath = *pathname ? pathname : vufs->target; retval = openat(vufs->rdirfd, filepath, flags, mode); break; case VUFSA_DOVIRT: /* openat O_EMPTY_PATH needed here */ filepath = *pathname ? pathname : vufs->source; if ((flags & O_CREAT) && oldmode == 0) { vufs_create_path(vufs->vdirfd, filepath, vufs_copyfile_create_path_cb, vufs); retval = openat(vufs->vdirfd, filepath, flags, mode); if (retval < 0) vufs_destroy_path(vufs->vdirfd, filepath); } else retval = openat(vufs->vdirfd, filepath, flags, mode); break; case VUFSA_DOCOPY: retval = vufs_copyfile(vufs, pathname, flags & O_TRUNC ? 0 : MAXSIZE); break; case VUFSA_FINAL: // open created this file if (retval >=0 && (flags & O_CREAT) && oldmode == 0) { // no need for whiteout file vufs_dewhiteout(vufs->ddirfd, pathname); vufs_newopenfilestat(vufs, pathname, retval, mode); } // fdprivate for getdents if (retval >= 0 && S_ISDIR(oldmode)) { int pathlen = strlen(pathname) + 1; struct vufs_fdprivate *vufs_fdprivate = malloc(sizeof(struct vufs_fdprivate) + pathlen); vufs_fdprivate->getdentsf = NULL; strcpy(vufs_fdprivate->path, pathname); *private = vufs_fdprivate; } else *private = NULL; break; case VUFSA_ERR: retval = -1; break; } } printkdebug(V, "OPEN path:%s flags:%o mode:%o retvalue:%d", pathname, flags, mode, retval); return retval; } int vu_vufs_close(int fd, void *fdprivate) { struct vufs_t *vufs = vu_get_ht_private_data(); int retval = 0; pthread_mutex_lock(&(vufs->mutex)); retval = close(fd); if (retval == 0 && fdprivate != NULL) { struct vufs_fdprivate *vufs_fdprivate = fdprivate; if (vufs_fdprivate->getdentsf != NULL) fclose(vufs_fdprivate->getdentsf); free(vufs_fdprivate); } pthread_mutex_unlock(&(vufs->mutex)); return retval; } vuos-0.9.2/vufs/vufstat.c000066400000000000000000000171741476575172100153720ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXT_CHAR 127 #define VSTATBUFLEN 1024 #define ARGMAXLEN 16 #define NELEM(X) (sizeof(X)/sizeof(*(X))) #define VUFSTATPATH(vpath, path) \ int _ ## path ## len = strlen(path) + 2; \ char vpath[_ ## path ## len]; \ snprintf(vpath, _ ## path ## len, "%s%c", (path), EXT_CHAR) static int vufstat_open(int dirfd, const char *path, int flags) { VUFSTATPATH(vstatpath, path); if (flags & O_CREAT) vufs_create_path(dirfd, vstatpath, NULL, NULL); return openat(dirfd, vstatpath, flags, 0644); } void vufstat_unlink(int dirfd, const char *path) { VUFSTATPATH(vstatpath, path); if (unlinkat(dirfd, vstatpath, 0) == 0) vufs_destroy_path(dirfd, vstatpath); } int vufstat_link(int dirfd, const char *oldpath, const char *newpath) { VUFSTATPATH(oldvstatpath, oldpath); VUFSTATPATH(newvstatpath, newpath); return linkat(dirfd, oldvstatpath, dirfd, newvstatpath, 0); } int vufstat_rename(int dirfd, const char *oldpath, const char *newpath, int flags) { VUFSTATPATH(oldvstatpath, oldpath); VUFSTATPATH(newvstatpath, newpath); return syscall(__NR_renameat2, dirfd, oldvstatpath, dirfd, newvstatpath, flags); } static void vufstat_read(int vstatfd, char *buf, size_t size) { ssize_t n = read(vstatfd, buf, size - 1); if (n < 0) n = 0; buf[n] = 0; } static inline int vufstat_stropt(const char *input, char **tags, char **args, char *buf) { return stroptx(input, NULL, "\n", 0, tags, args, buf); } static void merge_timespec(struct timespec *ts, const char *s) { struct timespec nts; char *tail; nts.tv_sec = strtoul(s, &tail, 0); if (*tail == ',') { tail ++; nts.tv_nsec = strtoul(tail, NULL, 0); } else nts.tv_nsec = 0; if (nts.tv_sec > ts->tv_sec || (nts.tv_sec == ts->tv_sec && nts.tv_nsec > ts->tv_nsec)) *ts = nts; } static dev_t read_dev_t(const char *s) { int maj; int min; char *tail; maj = strtoul(s, &tail, 0); if (*tail == ',') { tail ++; min = strtoul(tail, NULL, 0); } else min = 0; return makedev(maj,min); } uint32_t vufstat_merge(int dirfd, const char *path, struct vu_stat *statbuf) { int vstatfd = vufstat_open(dirfd, path, O_RDONLY); uint32_t mask = 0; if (vstatfd >= 0) { char input[VSTATBUFLEN]; int tagc; vufstat_read(vstatfd, input, VSTATBUFLEN); close(vstatfd); tagc = vufstat_stropt(input, NULL, NULL, 0); if(tagc > 0) { char *tags[tagc]; char *args[tagc]; vufstat_stropt(input, tags, args, input); for (int i = 0; tags[i]; i++) { if (args[i] != NULL) { switch (strcase(tags[i])) { case(STRCASE(t,y,p,e)): switch (args[i][0]) { case 's': statbuf->st_mode = (statbuf->st_mode & ~S_IFMT) | S_IFSOCK; mask |= VUFSTAT_TYPE; break; case '-': case 'f': statbuf->st_mode = (statbuf->st_mode & ~S_IFMT) | S_IFREG; mask |= VUFSTAT_TYPE; break; case 'c': statbuf->st_mode = (statbuf->st_mode & ~S_IFMT) | S_IFCHR; mask |= VUFSTAT_TYPE; break; case 'b': statbuf->st_mode = (statbuf->st_mode & ~S_IFMT) | S_IFBLK; mask |= VUFSTAT_TYPE; break; } break; case(STRCASE(m,o,d,e)): statbuf->st_mode = (statbuf->st_mode & S_IFMT) | (strtoul(args[i], NULL, 8) & ~S_IFMT); mask |= VUFSTAT_MODE; break; case(STRCASE(u,i,d)): statbuf->st_uid = strtoul(args[i], NULL, 0); mask |= VUFSTAT_UID; break; case(STRCASE(g,i,d)): statbuf->st_gid = strtoul(args[i], NULL, 0); mask |= VUFSTAT_GID; break; case(STRCASE(r,d,e,v)): statbuf->st_rdev = read_dev_t(args[i]); mask |= VUFSTAT_RDEV; break; case(STRCASE(d,e,v)): statbuf->st_dev = read_dev_t(args[i]); mask |= VUFSTAT_DEV; break; case(STRCASE(c,t,i,m,e)): merge_timespec(&statbuf->st_ctim, args[i]); mask |= VUFSTAT_CTIME; break; } } //printf("%s = %s\n",tags[i], args[i]); } } } return mask; } uint32_t vufstat_cmpstat(struct vu_stat *statbuf1, struct vu_stat *statbuf2) { uint32_t mask = 0; if (statbuf1->st_mode != statbuf2->st_mode) { if ((statbuf1->st_mode ^ statbuf2->st_mode) & S_IFMT) mask |= VUFSTAT_TYPE; if ((statbuf1->st_mode ^ statbuf2->st_mode) & ~S_IFMT) mask |= VUFSTAT_MODE; } if (statbuf1->st_uid != statbuf2->st_uid) mask |= VUFSTAT_UID; if (statbuf1->st_gid != statbuf2->st_gid) mask |= VUFSTAT_GID; if (statbuf1->st_rdev != statbuf2->st_rdev) mask |= VUFSTAT_RDEV; if (statbuf1->st_dev != statbuf2->st_dev) mask |= VUFSTAT_DEV; if (statbuf1->st_ctime != statbuf2->st_ctime) mask |= VUFSTAT_CTIME; return mask; } void vufstat_write(int dirfd, const char *path, struct vu_stat *statbuf, uint32_t mask) { if (mask == 0) vufstat_unlink(dirfd, path); else { int vstatfd = vufstat_open(dirfd, path, O_WRONLY | O_CREAT | O_TRUNC); if (vstatfd) { FILE *f = fdopen(vstatfd, "w+"); if (mask & VUFSTAT_TYPE) { mode_t mode = statbuf->st_mode; int chartype = 0; switch (mode & S_IFMT) { case S_IFREG: chartype = 'f'; break; case S_IFSOCK: chartype = 's'; break; case S_IFCHR: chartype = 'c'; break; case S_IFBLK: chartype = 'b'; break; } if (chartype) fprintf(f,"type=%c\n", chartype); } if (mask & VUFSTAT_MODE) fprintf(f,"mode=0%o\n", statbuf->st_mode & ~S_IFMT); if (mask & VUFSTAT_UID) fprintf(f,"uid=%d\n", statbuf->st_uid); if (mask & VUFSTAT_GID) fprintf(f,"gid=%d\n", statbuf->st_gid); if (mask & VUFSTAT_RDEV) fprintf(f,"rdev=%d,%d\n", major(statbuf->st_rdev), minor(statbuf->st_rdev)); if (mask & VUFSTAT_DEV) fprintf(f,"dev=%d,%d\n", major(statbuf->st_dev), minor(statbuf->st_dev)); if (mask & VUFSTAT_CTIME) fprintf(f,"ctime=%ld,%ld\n", statbuf->st_ctim.tv_sec, statbuf->st_ctim.tv_nsec); fclose(f); } } } void vufstat_update(int dirfd, const char *path, struct vu_stat *statbuf, uint32_t mask, mode_t creat) { struct vu_stat tmpbuf; int oldmask = vufstat_merge(dirfd, path, &tmpbuf); if (oldmask) { if (mask & VUFSTAT_TYPE) tmpbuf.st_mode = (tmpbuf.st_mode & ~S_IFMT) | (statbuf->st_mode & S_IFMT); if (mask & VUFSTAT_MODE) tmpbuf.st_mode = (tmpbuf.st_mode & S_IFMT) | (statbuf->st_mode & ~S_IFMT); if (mask & VUFSTAT_UID) tmpbuf.st_uid = statbuf->st_uid; if (mask & VUFSTAT_GID) tmpbuf.st_gid = statbuf->st_gid; if (mask & VUFSTAT_RDEV) tmpbuf.st_rdev = statbuf->st_rdev; if (mask & VUFSTAT_DEV) tmpbuf.st_dev = statbuf->st_dev; if (mask & VUFSTAT_CTIME) tmpbuf.st_ctime = statbuf->st_ctime; vufstat_write(dirfd, path, &tmpbuf, oldmask | mask); } else if (creat & O_CREAT) vufstat_write(dirfd, path, statbuf, mask); } vuos-0.9.2/vufs/vufstat.h000066400000000000000000000024251476575172100153700ustar00rootroot00000000000000#ifndef VUFSTAT_H #define VUFSTAT_H #include #include #include #include #include #include #define VUFSTAT_TYPE 0x00001 #define VUFSTAT_MODE 0x00002 #define VUFSTAT_UID 0x00010 #define VUFSTAT_GID 0x00020 #define VUFSTAT_RDEV 0x00100 #define VUFSTAT_DEV 0x00200 #define VUFSTAT_CTIME 0x01000 #define VUFSTAT_COPYMASK (VUFSTAT_MODE | VUFSTAT_UID | VUFSTAT_GID) uint32_t vufstat_merge(int dirfd, const char *path, struct vu_stat *statbuf); uint32_t vufstat_cmpstat(struct vu_stat *statbuf1, struct vu_stat *statbuf2); void vufstat_write(int dirfd, const char *path, struct vu_stat *statbuf, uint32_t mask); void vufstat_update(int dirfd, const char *path, struct vu_stat *statbuf, uint32_t mask, mode_t creat); #if 0 void vufstat_chmod(int dirfd, const char *path, mode_t mode); void vufstat_chown(int dirfd, const char *path, uid_t owner, gid_t group); void vufstat_mknod(int dirfd, const char *path, dev_t dev); void vufstat_settype(int dirfd, const char *path, mode_t mode); #endif void vufstat_unlink(int dirfd, const char *path); int vufstat_link(int dirfd, const char *oldpath, const char *newpath); int vufstat_rename(int dirfd, const char *oldpath, const char *newpath, int flags); #endif vuos-0.9.2/vufuse/000077500000000000000000000000001476575172100140525ustar00rootroot00000000000000vuos-0.9.2/vufuse/CMakeLists.txt000066400000000000000000000010371476575172100166130ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=314") include_directories(${VU_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}) set(VU_MOD_TARGET vufuse) file(GLOB_RECURSE VUFUSE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUFUSE_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} stropt volatilestream execs) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vufuse/fuse_opt.c000066400000000000000000000200131476575172100160360ustar00rootroot00000000000000/* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB */ #include #include #include #include #include struct fuse_opt_context { void *data; const struct fuse_opt *opt; fuse_opt_proc_t proc; int argctr; int argc; char **argv; struct fuse_args outargs; char *opts; int nonopt; }; void fuse_opt_free_args(struct fuse_args *args) { if (args) { if (args->argv && args->allocated) { int i; for (i = 0; i < args->argc; i++) free(args->argv[i]); free(args->argv); } args->argc = 0; args->argv = NULL; args->allocated = 0; } } static int alloc_failed(void) { fprintf(stderr, "fuse: memory allocation failed\n"); return -1; } int fuse_opt_add_arg(struct fuse_args *args, const char *arg) { char **newargv; char *newarg; assert(!args->argv || args->allocated); newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); newarg = newargv ? strdup(arg) : NULL; if (!newargv || !newarg) return alloc_failed(); args->argv = newargv; args->allocated = 1; args->argv[args->argc++] = newarg; args->argv[args->argc] = NULL; return 0; } static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, const char *arg) { assert(pos <= args->argc); if (fuse_opt_add_arg(args, arg) == -1) return -1; if (pos != args->argc - 1) { char *newarg = args->argv[args->argc - 1]; memmove(&args->argv[pos + 1], &args->argv[pos], sizeof(char *) * (args->argc - pos - 1)); args->argv[pos] = newarg; } return 0; } int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) { return fuse_opt_insert_arg_common(args, pos, arg); } int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg); int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg) { return fuse_opt_insert_arg_common(args, pos, arg); } static int next_arg(struct fuse_opt_context *ctx, const char *opt) { if (ctx->argctr + 1 >= ctx->argc) { fprintf(stderr, "fuse: missing argument after `%s'\n", opt); return -1; } ctx->argctr++; return 0; } static int add_arg(struct fuse_opt_context *ctx, const char *arg) { return fuse_opt_add_arg(&ctx->outargs, arg); } int fuse_opt_add_opt(char **opts, const char *opt) { char *newopts; if (!*opts) newopts = strdup(opt); else { unsigned oldlen = strlen(*opts); newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); if (newopts) { newopts[oldlen] = ','; strcpy(newopts + oldlen + 1, opt); } } if (!newopts) return alloc_failed(); *opts = newopts; return 0; } static int add_opt(struct fuse_opt_context *ctx, const char *opt) { return fuse_opt_add_opt(&ctx->opts, opt); } static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, int iso) { if (key == FUSE_OPT_KEY_DISCARD) return 0; if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); if (res == -1 || !res) return res; } if (iso) return add_opt(ctx, arg); else return add_arg(ctx, arg); } static int match_template(const char *t, const char *arg, unsigned *sepp) { int arglen = strlen(arg); const char *sep = strchr(t, '='); sep = sep ? sep : strchr(t, ' '); if (sep && (!sep[1] || sep[1] == '%')) { int tlen = sep - t; if (sep[0] == '=') tlen ++; if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { *sepp = sep - t; return 1; } } if (strcmp(t, arg) == 0) { *sepp = 0; return 1; } return 0; } static const struct fuse_opt *find_opt(const struct fuse_opt *opt, const char *arg, unsigned *sepp) { for (; opt && opt->templ; opt++) if (match_template(opt->templ, arg, sepp)) return opt; return NULL; } int fuse_opt_match(const struct fuse_opt *opts, const char *opt) { unsigned dummy; return find_opt(opts, opt, &dummy) ? 1 : 0; } static int process_opt_param(void *var, const char *format, const char *param, const char *arg) { assert(format[0] == '%'); if (format[1] == 's') { char *copy = strdup(param); if (!copy) return alloc_failed(); *(char **) var = copy; } else { if (sscanf(param, format, var) != 1) { fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg); return -1; } } return 0; } static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, unsigned sep, const char *arg, int iso) { if (opt->offset == -1U) { if (call_proc(ctx, arg, opt->value, iso) == -1) return -1; } else { void *var = ((char *)ctx->data) + opt->offset; if (sep && opt->templ[sep + 1]) { const char *param = arg + sep; if (opt->templ[sep] == '=') param ++; if (process_opt_param(var, opt->templ + sep + 1, param, arg) == -1) return -1; } else *(int *)var = opt->value; } return 0; } static int process_opt_sep_arg(struct fuse_opt_context *ctx, const struct fuse_opt *opt, unsigned sep, const char *arg, int iso) { int res; char *newarg; char *param; if (next_arg(ctx, arg) == -1) return -1; param = ctx->argv[ctx->argctr]; newarg = malloc(sep + strlen(param) + 1); if (!newarg) return alloc_failed(); memcpy(newarg, arg, sep); strcpy(newarg + sep, param); res = process_opt(ctx, opt, sep, newarg, iso); free(newarg); return res; } static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) { unsigned sep; const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); if (opt) { for (; opt; opt = find_opt(opt + 1, arg, &sep)) { int res; if (sep && opt->templ[sep] == ' ' && !arg[sep]) res = process_opt_sep_arg(ctx, opt, sep, arg, iso); else res = process_opt(ctx, opt, sep, arg, iso); if (res == -1) return -1; } return 0; } else return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); } static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) { char *sep; do { int res; sep = strchr(opts, ','); if (sep) *sep = '\0'; res = process_gopt(ctx, opts, 1); if (res == -1) return -1; opts = sep + 1; } while (sep); return 0; } static int process_option_group(struct fuse_opt_context *ctx, const char *opts) { int res; char *copy; const char *sep = strchr(opts, ','); if (!sep) return process_gopt(ctx, opts, 1); copy = strdup(opts); if (!copy) { fprintf(stderr, "fuse: memory allocation failed\n"); return -1; } res = process_real_option_group(ctx, copy); free(copy); return res; } static int process_one(struct fuse_opt_context *ctx, const char *arg) { if (ctx->nonopt || arg[0] != '-') return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); else if (arg[1] == 'o') { if (arg[2]) return process_option_group(ctx, arg + 2); else { if (next_arg(ctx, arg) == -1) return -1; return process_option_group(ctx, ctx->argv[ctx->argctr]); } } else if (arg[1] == '-' && !arg[2]) { if (add_arg(ctx, arg) == -1) return -1; ctx->nonopt = ctx->outargs.argc; return 0; } else return process_gopt(ctx, arg, 0); } static int opt_parse(struct fuse_opt_context *ctx) { if (ctx->argc) { if (add_arg(ctx, ctx->argv[0]) == -1) return -1; } for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) return -1; if (ctx->opts) { if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) return -1; } if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) { free(ctx->outargs.argv[ctx->outargs.argc - 1]); ctx->outargs.argv[--ctx->outargs.argc] = NULL; } return 0; } int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc) { int res; struct fuse_opt_context ctx = { .data = data, .opt = opts, .proc = proc, }; if (!args || !args->argv || !args->argc) return 0; ctx.argc = args->argc; ctx.argv = args->argv; res = opt_parse(&ctx); if (res != -1) { struct fuse_args tmp = *args; *args = ctx.outargs; ctx.outargs = tmp; } free(ctx.opts); fuse_opt_free_args(&ctx.outargs); return res; } vuos-0.9.2/vufuse/vufuse.c000066400000000000000000000266231476575172100155440ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * Leonardo Frioli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(vufuse) struct vu_module_t vu_module = { .name = "vufuse", .description = "vu virtual file systems (user level FUSE)" }; /* values for INUSE and thread synchro */ #define WAITING_FOR_LOOP -1 #define EXITING -2 #define FUSE_ABORT -3 static pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER; struct fusethreadopt { struct fuse *new_fuse; struct main_params main_params; }; int vufuse_abort(struct fuse *f) { f->inuse = FUSE_ABORT; pthread_mutex_lock( &condition_mutex ); pthread_cond_signal( &f->startloop ); pthread_mutex_unlock( &condition_mutex ); return 0; } static void *fusethread(void *vsmo) { struct fusethreadopt *psmo = (struct fusethreadopt *) vsmo; if (fusestartmain(&psmo->main_params) != 0) vufuse_abort(psmo->new_fuse); pthread_exit(NULL); return NULL; } /* There are two ways to notify vufuse that the submodule is reentrant: MAIN MODE: there is a symbol "fuse_reentrant_tag" in the library e.g int fuse_reentrant_tag = 0; SECONDARY/BACKUP MODE: add a file vdefusexxxxx.re ins the same directory where vdefusexxxxx.so is loaded */ static int secondary_reentrancy_test(void *handle) { struct link_map *lm; if (dlinfo(handle, RTLD_DI_LINKMAP, &lm) == 0) { int pathlen = strlen(lm->l_name) + 1; char tagpath[pathlen]; struct stat buf; snprintf(tagpath, pathlen, "%*.*s.re", pathlen - 4, pathlen - 4, lm->l_name); //printk("SECONDARY %s %s\n", lm->l_name, tagpath); if (stat(tagpath, &buf) == 0) return 1; } return 0; } int vu_vufuse_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { void *dlhandle; if ((dlhandle = vu_mod_dlopen(filesystemtype, RTLD_NOW | RTLD_NOLOAD)) != NULL) { if (dlsym(dlhandle, "fuse_reentrant_tag") == NULL && secondary_reentrancy_test(dlhandle) == 0) { printk("non-reentrant vufuse submodule %s already loaded\n", filesystemtype); errno = EBUSY; return -1; } } dlhandle = vu_mod_dlopen(filesystemtype, RTLD_NOW); int (*pmain)(int argc, char **argv); //printk("vu_vufuse_mount %s %s %s 0x%x %s\n", source, target, filesystemtype, mountflags, data); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" if(dlhandle == NULL || (pmain = dlsym(dlhandle,"main")) == NULL) { #pragma GCC diagnostic pop if (dlhandle != NULL) { printk(KERN_ERR "%s",dlerror()); dlclose(dlhandle); } errno = ENOSYS; return -1; } else { struct fusethreadopt smo; struct vu_service_t *s = vu_mod_getservice(); struct fuse *new_fuse; struct vuht_entry_t *ht; new_fuse = (struct fuse *)malloc(sizeof(struct fuse)); if (new_fuse == NULL) goto err_nomem_fuse; new_fuse->dlhandle = dlhandle; new_fuse->fops = vufuse_default_ops; new_fuse->mountflags = mountflags; new_fuse->fuseflags = 0; new_fuse->inuse = WAITING_FOR_LOOP; new_fuse->private_data = NULL; pthread_mutex_init(&(new_fuse->mutex), NULL); pthread_cond_init(&(new_fuse->startloop), NULL); pthread_cond_init(&(new_fuse->endloop), NULL); pthread_mutex_lock(&(new_fuse->mutex)); ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, new_fuse); vu_mod_setht(ht); smo.new_fuse = new_fuse; smo.main_params.pmain = pmain; smo.main_params.filesystemtype = filesystemtype; smo.main_params.source = source; smo.main_params.target = target; smo.main_params.pmountflags = &(new_fuse->mountflags); smo.main_params.pfuseflags = &(new_fuse->fuseflags); smo.main_params.opts = data ? (char *) data : ""; pthread_create(&(new_fuse->thread), NULL, fusethread, (void *)&smo); pthread_mutex_lock( &condition_mutex ); if (new_fuse->inuse== WAITING_FOR_LOOP) pthread_cond_wait( &(new_fuse->startloop), &condition_mutex); pthread_mutex_unlock( &condition_mutex ); if (new_fuse->inuse == FUSE_ABORT) goto err_startloop_fault; if (new_fuse->fops.init != NULL) { struct fuse_conn_info conn; struct fuse_context fcx, *ofcx; ofcx = fuse_push_context (&fcx); struct fuse_config cfg; /* XXX */ memset(&cfg, 0, sizeof(cfg)); new_fuse->private_data=new_fuse->fops.init(&conn, &cfg); fuse_pop_context(ofcx); } pthread_mutex_unlock(&(new_fuse->mutex)); printkdebug(F, "MOUNT source:%s target:%s filesystemtype:%s mountflags:%x data:%s", source,target,filesystemtype,mountflags, (data!=NULL)?data:""); return 0; err_startloop_fault: pthread_mutex_unlock(&(new_fuse->mutex)); pthread_join(new_fuse->thread, NULL); /* new and new_fuse as well as waiting for the thread to terminate done by cleanup */ vuht_del(ht,1); errno = EFAULT; /* temporary solution */ return -1; err_nomem_fuse: dlclose(dlhandle); errno = ENOMEM; return -1; } } static void vufuse_umount_internal(struct fuse *fuse) { if (fuse->fops.destroy != NULL ) { struct fuse_context fcx, *ofcx; ofcx = fuse_push_context (&fcx); fuse->fops.destroy(fuse->private_data); fuse_pop_context(ofcx); } pthread_mutex_lock( &condition_mutex ); fuse->inuse= EXITING; pthread_cond_signal(&fuse->endloop); pthread_mutex_unlock( &condition_mutex ); pthread_join(fuse->thread, NULL); pthread_cond_destroy(&(fuse->startloop)); pthread_cond_destroy(&(fuse->endloop)); pthread_mutex_destroy(&(fuse->mutex)); dlclose(fuse->dlhandle); free(fuse); } int vu_vufuse_umount2(const char *target, int flags) { struct fuse *fuse = vu_get_ht_private_data(); if (fuse == NULL) { errno = EINVAL; return -1; } else { pthread_mutex_lock(&(fuse->mutex)); if (fuse->inuse) { pthread_mutex_unlock(&(fuse->mutex)); errno = EBUSY; return -1; } else { int retval; /*cleanup and umount_internal will do the right umounting sequence in a lazy way*/ if ((retval = vuht_del(vu_mod_getht(),flags)) < 0) {; errno = -retval; retval = -1; } pthread_mutex_unlock(&(fuse->mutex)); printkdebug(F,"UMOUNT target:%s flags:%d retval = %d",target,flags,retval); return retval; } } } void vu_vufuse_cleanup(uint8_t type, void *arg, int arglen,struct vuht_entry_t *ht) { if (type == CHECKPATH) { struct fuse *fuse = vuht_get_private_data(ht); if (fuse == NULL) { errno = EINVAL; } else vufuse_umount_internal(fuse); } } /* management of context */ static __thread struct fuse_context *__fuse_context; struct fuse_context *fuse_push_context(struct fuse_context *new) { struct fuse_context *old_fuse_context = __fuse_context; struct fuse *fuse = vu_get_ht_private_data(); new->uid = geteuid(); new->gid = getegid(); new->pid = vu_mod_gettid(); new->umask = vu_mod_getumask(); new->fuse = fuse; new->private_data = fuse->private_data; __fuse_context = new; return old_fuse_context; } void fuse_pop_context(struct fuse_context *old) { __fuse_context = old; } /*******************************************************************************************/ /* fuse related functions*/ int fuse_version(void) { return FUSE_USE_VERSION;} struct fuse_context *fuse_get_context(void) { return __fuse_context; } struct fuse *__fuse_new(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data); int __fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data) { struct fuse *f; int res = fuse_mount(NULL, NULL); /*options have been already parsed*/ if (res != -1) { f = __fuse_new(NULL, op, op_size, user_data); return fuse_loop(f); } else return -1; } /* fuse_mount and fuse_unmount are dummy functions, * the real mount operation has been done in vufuse_mount */ int fuse_mount(struct fuse *f, const char *mountpoint) { return 0; } void fuse_unmount(struct fuse* f) { return; } /* mergefun: set non-null functions */ static void fopsmerge (const struct fuse_operations *fops, const struct fuse_operations *modfops, size_t size) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" const void **f = fops; const void **modf = modfops; #pragma GCC diagnostic pop size_t i; if (size > sizeof(struct fuse_operations)) size = sizeof(struct fuse_operations); size = size / sizeof(void *); for (i = 0; i inuse=FUSE_ABORT; } else { fuse->private_data = user_data; fopsmerge(&fuse->fops, op, op_size); } pthread_mutex_unlock( &condition_mutex ); return fuse; } void fuse_destroy(struct fuse *f) { /* ** * Destroy the FUSE handle. * * The filesystem is not unmounted. * * @param f the FUSE handle */ } int fuse_loop(struct fuse *f) { pthread_mutex_lock( &condition_mutex ); if (f == NULL) { f = vu_get_ht_private_data(); f->inuse = FUSE_ABORT; } if (f->inuse != FUSE_ABORT) f->inuse = 0; pthread_cond_signal( &f->startloop ); if (f->inuse != EXITING && f->inuse != FUSE_ABORT) pthread_cond_wait( &f->endloop, &condition_mutex ); pthread_mutex_unlock( &condition_mutex ); return 0; } void fuse_exit(struct fuse *f) { /** * Exit from event loop * * @param f the FUSE handle */ } #if FUSE_USE_VERSION < 32 int fuse_loop_mt(struct fuse *f, int clone_fd) { //in fuselib is FUSE event loop with multiple threads, //but hereeverything has multiple threads ;-) return fuse_loop(f); } #else int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config) { //in fuselib is FUSE event loop with multiple threads, //but hereeverything has multiple threads ;-) return fuse_loop(f); } #endif /* other dummy functions. useless for vufuse */ struct fuse_session *fuse_get_session(struct fuse *f) { return NULL; } int fuse_set_signal_handlers(struct fuse_session *se) { return 0; } void fuse_remove_signal_handlers(struct fuse_session *se) { } int fuse_daemonize(int foreground) { return 0; } int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, int *multithreaded, int *foreground) { *mountpoint = strdup(""); *multithreaded = 0; *foreground = 1; return 0; } /* constructor / destructor */ __attribute__((constructor)) static void init(void) { debug_set_name(F, "VUFUSE"); } __attribute__((destructor)) static void fini(void) { debug_set_name(F, ""); } vuos-0.9.2/vufuse/vufuse.h000066400000000000000000000027411476575172100155440ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #ifndef VUFUSE_H #define VUFUSE_H #include /** Enable hard remove */ #define FUSE_HARDREMOVE (1 << 0) struct fuse { void *dlhandle; struct fuse_operations fops; pthread_mutex_t mutex; pthread_t thread; pthread_cond_t startloop; pthread_cond_t endloop; int inuse; int fake_chan_fd; unsigned long mountflags; unsigned long fuseflags; void *private_data; }; struct fileinfo { //char *path; struct fuse_node *node; off_t pos; /* file offset */ struct fuse_file_info ffi; /* includes open flags, file handle and page_write mode */ FILE *dirf; }; struct fuse_context *fuse_push_context(struct fuse_context *new); void fuse_pop_context(struct fuse_context *old); #endif vuos-0.9.2/vufuse/vufuse_compat.c000066400000000000000000000025201476575172100170750ustar00rootroot00000000000000#include struct fuse_operations; struct fuse_args; struct libfuse_version; int __fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data); int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, void *user_data) { return __fuse_main_real(argc, argv, op, op_size, user_data); } int fuse_main_real_versioned(int argc, char *argv[], const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { return __fuse_main_real(argc, argv, op, op_size, user_data); } struct fuse *__fuse_new(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data); struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, void *user_data) { return __fuse_new(args, op, op_size, user_data); } struct fuse *_fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { return __fuse_new(args, op, op_size, user_data); } struct fuse *_fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, size_t op_size, struct libfuse_version *version, void *user_data) { return __fuse_new(args, op, op_size, user_data); } vuos-0.9.2/vufuse/vufuse_default_ops.c000066400000000000000000000151351476575172100201250ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * Leonardo Frioli * VirtualSquare team. * (inherited from umfuse Copyright 2005 Renzo Davoli) * * 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, see . * */ #include #include #include #include #include /* Check fuse.h for the documentation*/ static int vustd_getattr (const char *path, struct stat *stat, struct fuse_file_info *fi) { printkdebug(F,"DEFAULT getattr %s\n", path); return -ENOTSUP; } static int vustd_readlink (const char *path, char *link, size_t size) { printkdebug(F,"DEFAULT readlink %s\n", path); return -EINVAL; } static int vustd_mknod (const char *path, mode_t mode, dev_t dev) { printkdebug(F,"DEFAULT mknod %s\n", path); return -ENOSYS; } static int vustd_mkdir (const char *path, mode_t mode) { printkdebug(F,"DEFAULT mkdir %s\n", path); return -ENOSYS; } static int vustd_unlink (const char *path) { printkdebug(F,"DEFAULT unlink %s\n", path); return -ENOSYS; } static int vustd_rmdir (const char *path) { printkdebug(F,"DEFAULT rmdir %s\n", path); return -ENOSYS; } static int vustd_symlink (const char *path, const char *newpath) { printkdebug(F,"DEFAULT symlink %s\n", path); return -ENOSYS; } static int vustd_rename (const char *path, const char *newpath, unsigned int flags) { printkdebug(F,"DEFAULT rename %s\n", path); return -ENOSYS; } static int vustd_link (const char *path, const char *newpath) { printkdebug(F,"DEFAULT link %s\n", path); return -ENOSYS; } static int vustd_chmod (const char *path, mode_t mode, struct fuse_file_info *fi) { printkdebug(F,"DEFAULT chmod %s\n", path); return -ENOSYS; } static int vustd_chown (const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { printkdebug(F,"DEFAULT chown %s\n", path); return -ENOSYS; } static int vustd_truncate (const char *path, off_t off, struct fuse_file_info *fi) { printkdebug(F,"DEFAULT truncat %s\n", path); return -ENOSYS; } static int vustd_open (const char *path, struct fuse_file_info *fi) { printkdebug(F,"DEFAULT open %s\n", path); return -ENOSYS; } static int vustd_read (const char *path, char *buf, size_t size, off_t off, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT read %s\n", path); return -ENOSYS; } static int vustd_write (const char *path, const char *buf, size_t size, off_t off, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT write %s\n", path); return -ENOSYS; } /** Get file system statistics * * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored * */ #if ( FUSE_MINOR_VERSION >= 5 ) static int vustd_statfs (const char *path, struct statvfs *stat) #else static int vustd_statfs (const char *path, struct statfs *stat) #endif { printkdebug(F,"DEFAULT statfs %s\n", path); return -ENOSYS; } static int vustd_flush (const char *path, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT flush %s\n", path); return 0; //maybe flush is not relevant } static int vustd_release (const char *path, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT release %s\n", path); return 0; } static int vustd_fsync (const char *path, int flags, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT fsync %s\n", path); return 0; } /** Set extended attributes */ static int vustd_setxattr (const char *path, const char *name, const char *attr, size_t size, int flags) { printkdebug(F,"DEFAULT setxattr %s\n", path); return -ENOSYS; } /** Get extended attributes */ static int vustd_getxattr (const char *path, const char *name, char *attr, size_t size) { printkdebug(F,"DEFAULT getxattr %s\n", path); return -ENOSYS; } static int vustd_listxattr (const char *path, char *addrlist, size_t size) { printkdebug(F,"DEFAULT listxattr %s\n", path); return -ENOSYS; } static int vustd_removexattr (const char *path, const char *name) { printkdebug(F,"DEFAULT removexattr %s\n", path); return -ENOSYS; } static int vustd_opendir (const char *path, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT opendir %s\n", path); return 0; } static int vustd_releasedir (const char *path, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT removexattr %s\n", path); return 0; } static int vustd_fsyncdir (const char *path, int user_meta, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT fsyncdir %s\n", path); return -ENOSYS; } static int vustd_access (const char *path, int mode) { printkdebug(F,"DEFAULT access %s\n", path); return -ENOSYS; } static int vustd_create (const char *path, mode_t mode, struct fuse_file_info *fileinfo) { printkdebug(F,"DEFAULT create %s\n", path); return -ENOSYS; } static int vustd_lock (const char *path, struct fuse_file_info *fileinfo, int cmd, struct flock *fl) { printkdebug(F,"DEFAULT lock %s\n", path); return -ENOSYS; } static int vustd_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) { printkdebug(F,"DEFAULT utimens %s\n", path); return -ENOSYS; } static int vustd_bmap (const char *path, size_t blocksize, uint64_t *idx) { printkdebug(F,"DEFAULT bmap %s\n", path); return -ENOSYS; } struct fuse_operations vufuse_default_ops = { .getattr = vustd_getattr, .readlink = vustd_readlink, .mknod = vustd_mknod, .mkdir = vustd_mkdir, .unlink = vustd_unlink, .rmdir = vustd_rmdir, .symlink = vustd_symlink, .rename = vustd_rename, .link = vustd_link, .chmod = vustd_chmod, .chown = vustd_chown, .truncate = vustd_truncate, .open = vustd_open, .read = vustd_read, .write = vustd_write, .statfs = vustd_statfs, .flush = vustd_flush, .release = vustd_release, .fsync = vustd_fsync, .setxattr = vustd_setxattr, .getxattr = vustd_getxattr, .listxattr = vustd_listxattr, .removexattr = vustd_removexattr, .opendir = vustd_opendir, .releasedir = vustd_releasedir, .fsyncdir = vustd_fsyncdir, .init = NULL, .destroy = NULL, .access = vustd_access, .create = vustd_create, .lock = vustd_lock, .utimens = vustd_utimens, .bmap = vustd_bmap, }; vuos-0.9.2/vufuse/vufuse_default_ops.h000066400000000000000000000017351476575172100201330ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * (inherited from umfuse Copyright 2005 Renzo Davoli) * * 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, see . * */ #ifndef VUFUSE_DEFAULT_OPS_H #define VUFUSE_DEFAULT_OPS_H #include extern struct fuse_operations vufuse_default_ops; #endif vuos-0.9.2/vufuse/vufuse_node.c000066400000000000000000000122731476575172100165450ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * Leonardo Frioli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #define NODE_HASH_SIZE 128 #define NODE_HASH_MASK (NODE_HASH_SIZE-1) struct fuse_node { char *path; struct fuse *fuse; long hashsum; int open_count; struct fuse_node **pprevhash,*nexthash; }; static pthread_mutex_t vufuse_node_mutex = PTHREAD_MUTEX_INITIALIZER; static struct fuse_node *vufuse_node_head[NODE_HASH_SIZE]; #define ADDRESS_LEN ((int)(sizeof(uintptr_t) * 2)) #define HIDDEN_PREFIX_LEN (6 + ADDRESS_LEN) static char *vufuse_node_hiddenpath_rename(struct fuse *fuse, char *oldpath) { char *name; static unsigned long hiddencount; char *last_slash = strrchr(oldpath, '/'); int last_slash_pos = (last_slash == NULL) ? 0 : (last_slash - oldpath); if (asprintf(&name,"%*.*s/.fuse%0*lx%010lu", last_slash_pos, last_slash_pos, oldpath, ADDRESS_LEN, (uintptr_t)fuse, hiddencount++) < 0) return oldpath; free(oldpath); return name; } static inline int vufuse_node_hiddenpath_check(struct fuse *fuse, const char *path) { char *last_slash = strrchr(path, '/'); if (last_slash) { char check[HIDDEN_PREFIX_LEN + 1]; snprintf(check,HIDDEN_PREFIX_LEN + 1,"/.fuse%0*lx", ADDRESS_LEN, (uintptr_t) fuse); return (strncmp(last_slash, check, HIDDEN_PREFIX_LEN) == 0); } else return 0; } static inline long vufuse_node_hash_sum(struct fuse *fuse, const char *path) { long sum = (long) fuse; while (*path != 0) { sum ^= ((sum << 5) + (sum >> 2) + *path); path++; } return sum; } static inline int vufuse_node_hash_mod(long sum) { return sum & NODE_HASH_MASK; } static inline struct fuse_node *vufuse_node_find(void *fuse, const char *path, long hashsum, int hashkey) { struct fuse_node *scan=vufuse_node_head[hashkey]; //printk("node_find %s\n",path); while (scan != NULL) { //printk("node_find_scan %s\n",path,scan->path); if (scan->hashsum == hashsum && scan->fuse == fuse && strcmp(scan->path, path) == 0) return scan; scan=scan->nexthash; } return NULL; } char *vufuse_node_path(struct fuse_node *node) { return node->path; } struct fuse_node *vufuse_node_add(struct fuse *fuse, const char *path) { long hashsum = vufuse_node_hash_sum(fuse, path); int hashkey = vufuse_node_hash_mod(hashsum); struct fuse_node *new; pthread_mutex_lock(&vufuse_node_mutex); new = vufuse_node_find(fuse, path, hashsum, hashkey); if (new != NULL) new->open_count++; else { new = malloc(sizeof (struct fuse_node)); if (new != NULL) { new->path = strdup(path); new->fuse = fuse; new->hashsum = hashsum; new->open_count = 1; if (vufuse_node_head[hashkey] != NULL) vufuse_node_head[hashkey]->pprevhash = &(new->nexthash); new->nexthash = vufuse_node_head[hashkey]; new->pprevhash = &(vufuse_node_head[hashkey]); vufuse_node_head[hashkey] = new; } } pthread_mutex_unlock(&vufuse_node_mutex); return new; } char *vufuse_node_del(struct fuse_node *old) { char *ret_value = NULL; pthread_mutex_lock(&vufuse_node_mutex); if (old) { old->open_count--; if (old->open_count <= 0) { *(old->pprevhash)=old->nexthash; if (old->nexthash) old->nexthash->pprevhash=old->pprevhash; if (old->path) { if (vufuse_node_hiddenpath_check(old->fuse, old->path)) ret_value = old->path; else free(old->path); } free(old); } } pthread_mutex_unlock(&vufuse_node_mutex); return ret_value; } char *vufuse_node_rename(struct fuse *fuse, const char *path, const char *newpath) { long hashsum = vufuse_node_hash_sum(fuse, path); int hashkey = vufuse_node_hash_mod(hashsum); struct fuse_node *this; char *ret_value = NULL; pthread_mutex_lock(&vufuse_node_mutex); this = vufuse_node_find(fuse, path, hashsum, hashkey); if (this != NULL) { *(this->pprevhash)=this->nexthash; if (this->nexthash) this->nexthash->pprevhash=this->pprevhash; if (newpath == NULL) this->path = vufuse_node_hiddenpath_rename(fuse, this->path); else { free(this->path); this->path = strdup(newpath); } hashsum = vufuse_node_hash_sum(fuse, this->path); this->hashsum = hashsum; if (vufuse_node_head[hashkey] != NULL) vufuse_node_head[hashkey]->pprevhash = &(this->nexthash); this->nexthash = vufuse_node_head[hashkey]; this->pprevhash = &(vufuse_node_head[hashkey]); vufuse_node_head[hashkey] = this; ret_value = this->path; } pthread_mutex_unlock(&vufuse_node_mutex); return ret_value; } vuos-0.9.2/vufuse/vufuse_node.h000066400000000000000000000005201476575172100165420ustar00rootroot00000000000000#ifndef VUFUSE_NODE_H #define VUFUSE_NODE_H struct fuse; struct fuse_node; char *vufuse_node_path(struct fuse_node *node); struct fuse_node *vufuse_node_add(struct fuse *fuse, const char *path); char *vufuse_node_del(struct fuse_node *node); char *vufuse_node_rename(struct fuse *fuse, const char *path, const char *newpath); #endif vuos-0.9.2/vufuse/vufuse_startmain.c000066400000000000000000000074041476575172100176220ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #define FUSE_MOUNTFLAGS (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_SYNCHRONOUS | MS_REMOUNT | \ MS_MANDLOCK | MS_DIRSYNC | MS_NOATIME | MS_NODIRATIME | MS_POSIXACL | MS_RELATIME | MS_STRICTATIME | \ MS_LAZYTIME) static int countflagoptions(unsigned long mountflags) { int retval = 0; int i; for (i = 0; i < 32; i++) { if ((mountflags & (1UL << i)) && (mountflag_strings[i])) retval++; } return retval; } static void addflagoptions(char **tags, char **args, unsigned long mountflags) { int i; int flagoptc; for (i = 0, flagoptc = 0; i < 32; i++) { if ((mountflags & (1UL << i)) && (mountflag_strings[i])) { tags[flagoptc] = mountflag_strings[i]; args[flagoptc] = NULL; flagoptc++; } } tags[flagoptc] = NULL; args[flagoptc] = NULL; } int fusestartmain(struct main_params *mntp) { int tagc = stropt(mntp->opts, NULL, NULL, 0) - 1; int flagtagc = countflagoptions(*mntp->pmountflags & FUSE_MOUNTFLAGS); char buf[strlen(mntp->opts)+1]; char *newopts = NULL; char *format = "%N -o %O %S %T"; int i; int retval; #define copy_mntp_on_stack(string) \ size_t string ## _len = strlen(mntp->string) + 1; \ char string[string ## _len]; \ strncpy(string, mntp->string, string ## _len) copy_mntp_on_stack(filesystemtype); copy_mntp_on_stack(source); copy_mntp_on_stack(target); if(tagc + flagtagc == 0) newopts = strdup("rw"); else { int tags_args_len = tagc + flagtagc + 1; char *tags[tags_args_len]; char *args[tags_args_len]; stropt(mntp->opts, tags, args, buf); for (i = 0; i < tagc; i++) { //printf("%s =%s\n",tags[i],args[i]); switch(strcase(tags[i])) { case STRCASE(f,m,t): format = args[i]; tags[i] = STROPTX_DELETED_TAG; break; /* here some opt could change bits in mntp->pfuseflags */ case STRCASE(h,a,r,d,_,r,e,m,o,v,e): *mntp->pfuseflags |= FUSE_HARDREMOVE; tags[i] = STROPTX_DELETED_TAG; break; } } addflagoptions(tags+tagc, args+tagc, *mntp->pmountflags & FUSE_MOUNTFLAGS); newopts = stropt2str(tags, args, ',', '='); } //printf("NEWOPTS = %s\n", newopts); //printf("FORMAT = %s\n", format); char **xargv = s2argv(format); int xargc = s2argc(xargv); char *argv[xargc + 2]; int argc = 0; for (i = 0; i < xargc; i++) { switch(strcase(xargv[i])) { case STRCASE(perc,N): argv[argc++] = filesystemtype; break; case STRCASE(perc,O): argv[argc++] = newopts; break; case STRCASE(perc,S): argv[argc++] = source; break; case STRCASE(perc,T): argv[argc++] = target; break; default: argv[argc++] = xargv[i]; } } argv[argc] = NULL; #if 0 printf("ARG %s\n", mntp->opts); printf("argc %d\n", argc); for (i = 0; i < argc; i++) { printf("%i %s\n",i,argv[i]); } #endif optind = 1; retval = mntp->pmain(argc, argv); s2argv_free(xargv); free(newopts); return retval; } vuos-0.9.2/vufuse/vufuse_startmain.h000066400000000000000000000004701476575172100176230ustar00rootroot00000000000000#ifndef VUFUSE_STARTMAIN_H #define VUFUSE_STARTMAIN_H struct main_params { int (*pmain)(int argc, char **argv); const char *filesystemtype; const char *source; const char *target; unsigned long *pmountflags; unsigned long *pfuseflags; char *opts; }; int fusestartmain(struct main_params *mntp); #endif vuos-0.9.2/vufuse/vufuseop.c000066400000000000000000000546601476575172100161050ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * Leonardo Frioli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include VU_PROTOTYPES(vufuse) #define FILEPATH(x) vufuse_node_path(x->node) #define FFI(priv) (priv != NULL ? &((struct fileinfo *)(priv))->ffi : NULL) /*heuristics for file system which does not set st_ino */ static inline unsigned long hash_inodeno (const char *s) { unsigned long sum = 0; while (*s) { sum = sum ^ ((sum << 5) + (sum >> 2) + *s); s++; } return sum; } /* (vufuse_get_filesize callers need to hold the mutex */ static off_t vufuse_get_filesize(char *pathname) { struct fuse *fuse = vu_get_ht_private_data(); struct vu_stat buf; int rv; memset(&buf, 0, sizeof(struct vu_stat)); rv = fuse->fops.getattr(pathname, &buf, NULL); return (rv >= 0) ? buf.st_size : 0; } int vu_vufuse_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *private) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); memset(buf, 0, sizeof(struct vu_stat)); rv = fc.fuse->fops.getattr(pathname, buf, FFI(private)); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"LSTAT path:%s retvalue:%d", pathname, rv); if (rv < 0) { errno = -rv; return -1; } else { /*heuristics for file system which does not set st_ino */ if (buf->st_ino == 0) buf->st_ino = (ino_t) hash_inodeno(pathname); /*heuristics for file system which does not set st_dev */ if (buf->st_dev == 0) buf->st_dev = (dev_t)((unsigned long) fc.fuse); } return rv; } int vu_vufuse_access(char *path, int mode, int flags) { int rv = 0; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); /* "default permission" management */ rv = fc.fuse->fops.access(path, mode); if (rv == -ENOSYS) { struct vu_stat buf; // Pass fileinfo instead of null if the file is open rv = fc.fuse->fops.getattr(path, &buf, NULL); } fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"ACCESS path:%s mode:%s%s%s%s retvalue:%d",path, (mode & R_OK) ? "R_OK": "", (mode & W_OK) ? "W_OK": "", (mode & X_OK) ? "X_OK": "", (mode & F_OK) ? "F_OK": "", rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } ssize_t vu_vufuse_readlink(char *path, char *buf, size_t bufsiz) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.readlink(path, buf, bufsiz); pthread_mutex_unlock(&(fc.fuse->mutex)); if (rv == 0) rv = strnlen(buf,bufsiz); fuse_pop_context(ofc); printkdebug(F,"READLINK path:%s buf:%s retvalue:%zd",path,buf,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } #define FUSE_SUPER_MAGIC 0x65735546 int vu_vufuse_statfs (const char *pathname, struct statfs *buf, int fd, void *fdprivate) { struct fuse_context fc, *ofc; int rv; struct statvfs svfs; ofc = fuse_push_context(&fc); printkdebug(F,"STATFS", NULL); pthread_mutex_lock(&(fc.fuse->mutex)); memset (&svfs, 0, sizeof(struct statvfs)); rv = fc.fuse->fops.statfs(pathname, &svfs); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); if (rv >= 0) { buf->f_type = FUSE_SUPER_MAGIC; // buf->f_bsize = svfs.f_bsize; buf->f_blocks = svfs.f_blocks; buf->f_bfree = svfs.f_bfree; buf->f_bavail = svfs.f_bavail; buf->f_files = svfs.f_files; buf->f_ffree = svfs.f_ffree; buf->f_namelen =svfs.f_namemax; buf->f_frsize =svfs.f_frsize; /* fsid is left zero */ return rv; } else { errno = -rv; return -1; } } int vu_vufuse_mkdir (const char *pathname, mode_t mode) { int rv = 0; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.mkdir(pathname, mode); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"MKDIR path:%s retvalue:%d",pathname,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_mknod (const char *pathname, mode_t mode, dev_t dev) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); if (S_ISREG(mode)) { struct fuse_file_info fi; memset(&fi, 0, sizeof(fi)); fi.flags = O_CREAT | O_EXCL | O_WRONLY; rv = fc.fuse->fops.create(pathname, mode, &fi); if (rv >= 0) { fc.fuse->fops.release(pathname, &fi); } else if (rv == -ENOSYS) rv = fc.fuse->fops.mknod(pathname, mode, dev); } else rv = fc.fuse->fops.mknod(pathname, mode, dev); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"MKNOD path:%s major:%d minor:%d retvalue:%d",pathname,major(dev),minor(dev),rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_rmdir(const char *pathname) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.rmdir( pathname); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"RMDIR path:%s retvalue:%d",pathname,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_chmod (const char *pathname, mode_t mode, int fd, void *fdprivate) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.chmod(pathname, mode, FFI(fdprivate)); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"CHMOD path:%s retvalue:%d",pathname,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_lchown (const char *pathname, uid_t owner, gid_t group,int fd, void *fdprivate) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.chown(pathname, owner, group, FFI(fdprivate)); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"LCHOWN retvalue:%d",rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_symlink (const char *target, const char *linkpath) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.symlink( target, linkpath); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"SYMLINK target:%s linkpath:%s retvalue:%d",target,linkpath,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_truncate(const char *path, off_t length, int fd, void *fdprivate) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.truncate(path, length, FFI(fdprivate)); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"TRUNCATE path:%s retvalue:%d",path,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_link (const char *target, const char *linkpath) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.link(target, linkpath); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"LINK oldpath:%s newpath:%s retvalue:%d",target,linkpath,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_open(const char *pathname, int flags, mode_t mode, void **private) { int rv; int exists_err; struct fileinfo *ft; struct vu_stat buf; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); exists_err = fc.fuse->fops.getattr(pathname, &buf, NULL); /* if 0 the file already exists.*/ if ((flags & O_ACCMODE) != O_RDONLY && fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; pthread_mutex_unlock(&(fc.fuse->mutex)); return -1; } if ( (flags & (O_DIRECTORY)) && (!S_ISDIR(buf.st_mode))) { fuse_pop_context(ofc); errno = ENOTDIR; pthread_mutex_unlock(&(fc.fuse->mutex)); return -1; } if (exists_err == 0) { /* the file exists*/ if ((flags & O_ACCMODE) != O_RDONLY && (S_ISDIR(buf.st_mode))) { fuse_pop_context(ofc); printkdebug(F,"OPEN S_ISDIR %x", buf.st_mode); errno = EISDIR; pthread_mutex_unlock(&(fc.fuse->mutex)); return -1; } if ((flags & O_CREAT) && (flags & O_EXCL)) { errno = EEXIST; pthread_mutex_unlock(&(fc.fuse->mutex)); return -1; } if ((flags & O_TRUNC) && (flags & O_ACCMODE)!= O_RDONLY) { rv = fc.fuse->fops.truncate(pathname, 0, NULL); printkdebug(F,"TRUNCATE path:%s flags:%x retvalue:%d",pathname,flags,rv); if (rv < 0) { fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); errno = -rv; return -1; } } } ft = malloc(sizeof(struct fileinfo)); ft->pos = 0; ft->ffi.flags = flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); ft->ffi.writepage = 0; ft->node = NULL; ft->dirf = NULL; *private = NULL; /* create the file: create or (obsolete mode) mknod+open */ if ((flags & O_CREAT) && (exists_err != 0)) { rv = fc.fuse->fops.create(pathname, S_IFREG | mode, &ft->ffi); if (rv == -ENOSYS) { rv = fc.fuse->fops.mknod(pathname, S_IFREG | mode, (dev_t) 0); printkdebug(F,"MKNOD path:%s flags:%x retvalue:%d",pathname,flags,rv); if (rv < 0) { fuse_pop_context(ofc); free(ft); errno = -rv; pthread_mutex_unlock(&(fc.fuse->mutex)); return -1; } rv = fc.fuse->fops.open(pathname, &ft->ffi); } else { printkdebug(F,"CREATE path:%s flags:%x retvalue:%d",pathname,flags,rv); } #if 0 /* this should be useless */ if (rv >= 0) { if (fc.fuse->fops.fgetattr != NULL) rv = fc.fuse->fops.fgetattr(pathname, &buf, &ft->ffi); else rv = fc.fuse->fops.getattr(pathname, &buf); } #endif } else { /* the file exists! */ if (flags & O_DIRECTORY && fc.fuse->fops.readdir != NULL) { rv = fc.fuse->fops.opendir(pathname, &ft->ffi); if (rv == -ENOSYS) rv = fc.fuse->fops.open(pathname, &ft->ffi); } else rv = fc.fuse->fops.open(pathname, &ft->ffi); } fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"OPEN path:%s flags:%x retvalue:%d",pathname,flags,rv); if (rv < 0) { free(ft); errno = -rv; return -1; } else { ft->node = vufuse_node_add(fc.fuse, pathname); fc.fuse->inuse++; *private = ft; /*file related function will check sfd >= 0 before accessing fdprivate. That sfd will have the value of rv, so returning 0 is ok*/ return rv; } } int vu_vufuse_close(int fd, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { int rv; struct fileinfo *ft = (struct fileinfo *)fdprivate; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); if (!(ft->ffi.flags & O_DIRECTORY)) { rv = fc.fuse->fops.flush(FILEPATH(ft), &ft->ffi); } fc.fuse->inuse--; if ((ft->ffi.flags & O_DIRECTORY) && fc.fuse->fops.readdir != NULL) { rv = fc.fuse->fops.releasedir(FILEPATH(ft), &ft->ffi); } else rv = fc.fuse->fops.release(FILEPATH(ft), &ft->ffi); if (rv >= 0) { char *hiddenfile = vufuse_node_del(ft->node); if (hiddenfile) { fc.fuse->fops.unlink(hiddenfile); free(hiddenfile); } } fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"CLOSE retvalue:%d", rv); if (rv < 0) { errno = -rv; return -1; } else { if (ft->dirf) fclose(ft->dirf); free(fdprivate); fdprivate = NULL; return rv; } } } off_t vu_vufuse_lseek(int fd, off_t offset, int whence, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { struct fileinfo *ft =(struct fileinfo *)fdprivate; struct fuse *fuse = vu_get_ht_private_data(); printkdebug(F,"LSEEK path:%s offset:%jd whence:%d", FILEPATH(ft), (intmax_t)offset, whence); pthread_mutex_lock(&(fuse->mutex)); switch (whence) { case SEEK_SET: ft->pos = offset; break; case SEEK_CUR: ft->pos += offset; break; case SEEK_END: ft->pos = vufuse_get_filesize(FILEPATH(ft)) + offset; } pthread_mutex_unlock(&(fuse->mutex)); return ft->pos; } } ssize_t vu_vufuse_read (int fd, void *buf, size_t count, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { int rv; struct fileinfo *ft = (struct fileinfo *)fdprivate; if ((ft->ffi.flags & O_ACCMODE) == O_WRONLY) { errno = EBADF; return -1; } else { struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.read(FILEPATH(ft), buf, count, ft->pos, &ft->ffi); fuse_pop_context(ofc); if (rv >= 0) ft->pos += rv; pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"READ path:%s count:%u retvalue:%zd",FILEPATH(ft), count,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } } } ssize_t vu_vufuse_pread64 (int fd, void *buf, size_t count, off_t offset, int flags, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { int rv; struct fileinfo *ft = (struct fileinfo *)fdprivate; if ((ft->ffi.flags & O_ACCMODE) == O_WRONLY) { errno = EBADF; return -1; } else { struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.read(FILEPATH(ft), buf, count, offset, &ft->ffi); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"PREAD64 path:%s count:%u offset:%jd retvalue:%zd",FILEPATH(ft), count, (intmax_t) offset, rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } } } ssize_t vu_vufuse_write(int fd, const void *buf, size_t count, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { int rv = 0; struct fileinfo *ft = (struct fileinfo *)fdprivate; if ((ft->ffi.flags & O_ACCMODE) == O_RDONLY) { errno = EBADF; return -1; } else { struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); if (ft->ffi.flags & O_APPEND) ft->pos = vufuse_get_filesize(FILEPATH(ft)); rv = fc.fuse->fops.write(FILEPATH(ft), buf, count, ft->pos, &ft->ffi); fuse_pop_context(ofc); if (rv >= 0) ft->pos += rv; pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"WRITE path:%s count:%x retvalue:%d",FILEPATH(ft),count, rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } } } ssize_t vu_vufuse_pwrite64(int fd, const void *buf, size_t count, off_t offset, int flags, void *fdprivate) { if (fd < 0 || fdprivate == NULL) { errno = EBADF; return -1; } else { int rv = 0; struct fileinfo *ft = (struct fileinfo *)fdprivate; if ((ft->ffi.flags & O_ACCMODE) == O_RDONLY) { errno = EBADF; return -1; } else { struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.write(FILEPATH(ft), buf, count, offset, &ft->ffi); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"PWRITE64 path:%s count:%x offset:%jd retvalue:%zd", FILEPATH(ft), count, rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } } } static int vufuse_common_filldir(FILE *f, const char *name, unsigned char type, __ino64_t ino) { /* glibc hides enries having d_ino == 0 */ struct dirent64 entry = { .d_ino = ino == 0 ? (ino_t) -1 : ino, .d_type = type, .d_off = ftello(f), }; static char filler[7]; unsigned short int namelen = strlen(name) + 1; unsigned short int reclen = offsetof(struct dirent64, d_name) + namelen; int ret_value; snprintf(entry.d_name, 256, "%s", name); /* entries are always 8 bytes aligned */ entry.d_reclen = (reclen + 7) & (~7); ret_value = fwrite(&entry, reclen, 1, f); /* add a filler to align the next entry */ if (entry.d_reclen > reclen) ret_value += fwrite(filler, entry.d_reclen - reclen, 1, f); return 0; } /* Function to add an entry in a readdir() operation */ static int vufusefillreaddir(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags) { FILE *f = buf; __ino64_t d_ino; unsigned char d_type; if (stbuf == NULL) { d_ino = -1; d_type = 0; } else { d_ino = stbuf->st_ino; d_type = stbuf->st_mode >> 12; } return vufuse_common_filldir(f, name, d_type, d_ino); } struct fuse_dirhandle { FILE *f; }; int vu_vufuse_getdents64(unsigned int fd, struct dirent64 *dirp, unsigned int count, void *fdprivate) { if (fdprivate == NULL) { errno = EBADF; return -1; } else { struct fileinfo *ft = (struct fileinfo *)fdprivate; struct fuse *fuse = vu_get_ht_private_data(); size_t freadout; printkdebug(F,"GETDENTS", NULL); if (ft->dirf == NULL) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); pthread_mutex_lock(&(fc.fuse->mutex)); ft->dirf = volstream_open(); rv = fc.fuse->fops.readdir(FILEPATH(ft), ft->dirf, vufusefillreaddir, 0, &ft->ffi, 0); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); if (rv < 0) { fclose(ft->dirf); return 0; } else fseek(ft->dirf, 0, SEEK_SET); } pthread_mutex_lock(&(fuse->mutex)); freadout = fread(dirp, 1, count, ft->dirf); /* if the buffer is full the last entry might be incomplete. update freadout to drop the last incomplete entry, and seek back the position in the file to reread it from its beginning at the next getdents64 */ if (freadout == count) { unsigned int bpos = 0; struct dirent64 *d; char *buf = (char *) dirp; while (1) { d = (struct dirent64 *) (buf + bpos); if (count - bpos < offsetof(struct dirent64, d_name)) break; if (bpos + d->d_reclen > count) break; bpos += d->d_reclen; } if (bpos < count) { fseeko(ft->dirf, - (int) (count - bpos), SEEK_CUR); freadout -= count - bpos; } /* the buffer is so short that it does not fit one entry. Return EINVAL! */ if (freadout == 0) { pthread_mutex_unlock(&(fuse->mutex)); errno = EINVAL; return -1; } } pthread_mutex_unlock(&(fuse->mutex)); return freadout; } } int vu_vufuse_unlink (const char *pathname) { int rv; struct fuse_context fc, *ofc; struct vu_stat buf; ofc = fuse_push_context(&fc); char *hiddenpath = NULL; if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); if (fc.fuse->fops.getattr(pathname, &buf, NULL) < 0) { pthread_mutex_unlock(&(fc.fuse->mutex)); fuse_pop_context(ofc); errno = ENOENT; return -1; } if (fc.fuse->fuseflags & FUSE_HARDREMOVE || (hiddenpath = vufuse_node_rename(fc.fuse, pathname, NULL)) == NULL || (rv = fc.fuse->fops.rename(pathname, hiddenpath, 0)) < 0) { if (hiddenpath) vufuse_node_rename(fc.fuse, hiddenpath, pathname); rv = fc.fuse->fops.unlink(pathname); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"UNLINK path:%s retvalue:%d",pathname, rv); } else { pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"RENAME(UNLINK) path:%s hiddenpath:%s retvalue:%d",pathname,hiddenpath,rv); } fuse_pop_context(ofc); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_rename (const char *target, const char *linkpath, int flags) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); char *hiddenpath = NULL; if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); if (fc.fuse->fuseflags & FUSE_HARDREMOVE || (hiddenpath = vufuse_node_rename(fc.fuse, linkpath, NULL)) == NULL || fc.fuse->fops.rename(linkpath, hiddenpath, flags) < 0) { if (hiddenpath) { vufuse_node_rename(fc.fuse, hiddenpath, linkpath); hiddenpath = NULL; } } rv = fc.fuse->fops.rename(target, linkpath, flags); if (rv >= 0) vufuse_node_rename(fc.fuse, target, linkpath); else if (hiddenpath) { // revert the renaming to hiddenpath if (fc.fuse->fops.rename(hiddenpath, linkpath, flags) >= 0) vufuse_node_rename(fc.fuse, hiddenpath, linkpath); } pthread_mutex_unlock(&(fc.fuse->mutex)); fuse_pop_context(ofc); printkdebug(F,"RENAME oldpath:%s newpath:%s retvalue:%d",target,linkpath,rv); if (rv < 0) { errno = -rv; return -1; } else return rv; } int vu_vufuse_utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags, int fd, void *private) { int rv; struct fuse_context fc, *ofc; ofc = fuse_push_context(&fc); if (fc.fuse->mountflags & MS_RDONLY) { fuse_pop_context(ofc); errno = EROFS; return -1; } pthread_mutex_lock(&(fc.fuse->mutex)); rv = fc.fuse->fops.utimens(pathname, times, FFI(private)); fuse_pop_context(ofc); pthread_mutex_unlock(&(fc.fuse->mutex)); printkdebug(F,"UTIME path:%s", pathname); if (rv < 0) { errno = -rv; return -1; } else return rv; } vuos-0.9.2/vufuse_modules/000077500000000000000000000000001476575172100156025ustar00rootroot00000000000000vuos-0.9.2/vufuse_modules/CMakeLists.txt000066400000000000000000000027111476575172100203430ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${VU_DYN_HEADER_PATH}) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VUFUSE_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/vu*.c) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64") foreach(VUFUSE_MOD ${VUFUSE_MODULES}) string(REGEX REPLACE "\.c$" "" VUFUSE_MOD_FILE ${VUFUSE_MOD}) get_filename_component(VUFUSE_MOD_TARGET ${VUFUSE_MOD_FILE} NAME) add_library(${VUFUSE_MOD_TARGET} SHARED ${VUFUSE_MOD}) set_target_properties(${VUFUSE_MOD_TARGET} PROPERTIES PREFIX "") target_compile_definitions(${VUFUSE_MOD_TARGET} PUBLIC PROGNAME="${VUFUSE_MOD_TARGET}") install(TARGETS ${VUFUSE_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) endforeach(VUFUSE_MOD) foreach(VUFUSE_MOD ${VUFUSE_MODULES}) string(REGEX REPLACE "\.c$" "" VUFUSE_MOD_FILE ${VUFUSE_MOD}) get_filename_component(VUFUSE_MOD_EXE ${VUFUSE_MOD_FILE} NAME) string(REGEX REPLACE "^vu" "" VUFUSE_MOD_EXE ${VUFUSE_MOD_EXE}) set(VUFUSE_MOD_TARGET ${VUFUSE_MOD_EXE}) add_executable(${VUFUSE_MOD_TARGET} ${VUFUSE_MOD}) set_target_properties(${VUFUSE_MOD_TARGET} PROPERTIES OUTPUT_NAME ${VUFUSE_MOD_EXE}) target_compile_definitions(${VUFUSE_MOD_TARGET} PUBLIC PROGNAME="${VUFUSE_MOD_TARGET}") target_link_libraries(${VUFUSE_MOD_TARGET} -lfuse3) install(TARGETS ${VUFUSE_MOD_TARGET} RUNTIME DESTINATION bin) endforeach(VUFUSE_MOD) vuos-0.9.2/vufuse_modules/vufusenull.c000066400000000000000000000040351476575172100201600ustar00rootroot00000000000000/** * Copyright (c) 2018 Renzo Davoli * Leonardo Frioli * * 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 (in the main directory of the vuos * distribution in the file COPYING); if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 14) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif int fuse_reentrant_tag = 0; int op_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi){ if (strcmp(path, "/") == 0) { memset(stbuf, 0, sizeof(*stbuf)); stbuf->st_mode = 0755 | S_IFDIR; stbuf->st_nlink = 2; return 0; } else return -ENOENT; } int op_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags){ filler(buf, ".", NULL, 0, 0); filler(buf, "..", NULL, 0, 0); return 0; } static const struct fuse_operations null_ops = { .getattr = op_getattr, .readdir = op_readdir, }; int main(int argc, char *argv[]) { return fuse_main(argc, argv, &null_ops, NULL); } vuos-0.9.2/vufuse_modules/vufusereal.c000066400000000000000000000247401476575172100201360ustar00rootroot00000000000000/** * Copyright (c) 2018 Renzo Davoli * Leonardo Frioli * * 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 (in the main directory of the vuos * distribution in the file COPYING); if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 14) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PATH_MAX #define PATH_MAX 4096 #endif int fuse_reentrant_tag = 0; #define GETPATH(source, path) \ char path ## _fullpath [PATH_MAX]; \ snprintf((path ## _fullpath), PATH_MAX, "%s%s", (strcmp(source,"/")) ? source : "", path); \ path = path ## _fullpath #define RETURN(retvalue) return ((retvalue < 0) ? -errno : retvalue) #define RETURNZER0(retvalue) return ((retvalue < 0) ? -errno : 0) void * op_init (struct fuse_conn_info *conn, struct fuse_config *cfg){ struct fuse_context *cntx=fuse_get_context(); return cntx->private_data; } void op_destroy(void *userdata){ return; } int op_access(const char *path, int mask){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = access(path, mask); RETURN(rv); } int op_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = lstat(path,stbuf); RETURN(rv); } int op_getxattr(const char *path, const char *name, char *value, size_t size){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = getxattr (path, name, (void *)value, size); RETURN(rv); } int op_open(const char *path, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int fd = open(path,fi->flags); fi->fh = (uint64_t) fd; /*on success open must return 0 otherwise ERANGE is given*/ RETURNZER0(fd); } int op_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi){ int fd = (int) fi->fh; int rv = pread(fd, buf, size, offset); RETURN(rv); } int op_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags){ struct fuse_context *cntx = fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); struct dirent* de; int fd = dup((int)fi->fh); if(fd < 0) return -errno; DIR *dr = fdopendir(fd); if(dr == NULL) return -errno; while ((de = readdir(dr)) != NULL){ struct stat stbuf ; char filename[PATH_MAX]; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-truncation" snprintf(filename, PATH_MAX, "%s%s", (strcmp(path,"/")) ? path : "", de->d_name) ; #pragma GCC diagnostic pop //ignoring offset if (lstat(filename, &stbuf) >= 0) filler(buf, de->d_name, &stbuf, 0, 0); else filler(buf, de->d_name, NULL, 0, 0); //test for return value 1 ? } closedir(dr); return 0; } int op_readlink(const char *path, char *buf, size_t size){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); memset(buf,'\0',size); int rv = readlink(path,buf,size); RETURNZER0(rv); } int op_release(const char *path, struct fuse_file_info *fi){ //close int fd = (int) fi->fh; fi->fh = (uint64_t) -1; // correct? int rv = close(fd); RETURN(rv); } int op_statfs(const char *path, struct statvfs *buf){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = statvfs(path,buf); RETURN(rv); } int op_chmod(const char *path, mode_t mode, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = chmod(path,mode); RETURN(rv); } int op_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = chown(path,uid,gid); RETURN(rv); } int op_create(const char *path, mode_t mode, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int fd = open(path,fi->flags | O_CREAT ,mode); fi->fh = (uint64_t) fd; RETURNZER0(fd); } int op_flush(const char *path, struct fuse_file_info *fi){ /* * do nothing */ return 0; } int op_fsync(const char *path, int datasync, struct fuse_file_info *fi){ /* * do nothing */ return 0; } int op_mkdir(const char *path, mode_t mode){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = mkdir(path,mode|S_IFDIR); RETURN(rv); } int op_rmdir(const char *path){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = rmdir(path); RETURN(rv); } int op_unlink(const char *path){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = unlink(path); RETURN(rv); } int op_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = utimensat(AT_FDCWD,path,tv,0); RETURN(rv); } int op_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi){ int fd = (int) fi->fh; int rv = pwrite(fd, buf, size, offset); RETURN(rv); } int op_mknod(const char *path, mode_t mode, dev_t dev){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = mknod(path,mode,dev); RETURN(rv); } int op_symlink(const char *sourcename, const char *destname){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, destname); int rv = symlink(sourcename,destname); RETURN(rv); } int op_truncate(const char *path, off_t length, struct fuse_file_info *fi){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, path); int rv = truncate(path,length); RETURN(rv); } //only allowed hardlink internal to the mount point int op_link (const char *source, const char *dest){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, source); GETPATH(sourcepath, dest); int rv = link(source,dest); RETURN(rv); } //called only on internal rename int op_rename(const char *source, const char *dest, unsigned int flags){ struct fuse_context *cntx=fuse_get_context(); char *sourcepath = cntx->private_data; GETPATH(sourcepath, source); GETPATH(sourcepath, dest); int rv = rename(source,dest); RETURN(rv); } static const struct fuse_operations real_ops = { .getattr = op_getattr, .readlink = op_readlink, .mknod = op_mknod, .mkdir = op_mkdir, .unlink = op_unlink, .rmdir = op_rmdir, .symlink = op_symlink, .rename = op_rename, .link = op_link, .chmod = op_chmod, .chown = op_chown, .truncate = op_truncate, .open = op_open, .read = op_read, .write = op_write, .statfs = op_statfs, .flush = op_flush, .release = op_release, .fsync = op_fsync, .setxattr = NULL, .getxattr = op_getxattr, .listxattr = NULL, .removexattr = NULL, .opendir = op_open, .readdir = op_readdir, .releasedir = op_release, .fsyncdir = op_fsync, .init = op_init, .destroy = op_destroy, .access = op_access, .create = op_create, .lock = NULL, .utimens = op_utimens, .bmap = NULL, }; static void usage(void) { fprintf(stderr, "usage: " PROGNAME " sourcepath mountpoint [options]\n" "\n" "general options:\n" " -h --help print help\n" " -V --version print version\n" "\n"); } struct options { const char *source; const char *mountpoint; }; static int real_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) { struct options *options = data; switch(key) { case FUSE_OPT_KEY_OPT: return 1; case FUSE_OPT_KEY_NONOPT: if (!options->source) { options->source = arg; return 0; } else if(!options->mountpoint) { options->mountpoint = arg; return 1; } else return -1; break; case 'h': usage(); fuse_opt_add_arg(outargs, "-ho"); fuse_main(outargs->argc, outargs->argv, &real_ops, NULL); return -1; case 'V': fprintf(stderr, PROGNAME "\n"); fuse_opt_add_arg(outargs, "--version"); fuse_main(outargs->argc, outargs->argv, &real_ops, NULL); return -1; default: return -1; } } static struct fuse_opt real_opts[] = { FUSE_OPT_KEY("-V", 'V'), FUSE_OPT_KEY("--version", 'V'), FUSE_OPT_KEY("-h", 'h'), FUSE_OPT_KEY("--help", 'h'), FUSE_OPT_END }; int main(int argc, char *argv[]) { int err; struct options options = {0}; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); if (fuse_opt_parse(&args, &options, real_opts, real_opt_proc) == -1) { fuse_opt_free_args(&args); return -1; } if (options.source == NULL || options.mountpoint == NULL) { usage(); goto returnerr; } err = fuse_main(args.argc, args.argv, &real_ops, (void *) options.source); fuse_opt_free_args(&args); return err; returnerr: fuse_opt_free_args(&args); return -1; } vuos-0.9.2/vumisc/000077500000000000000000000000001476575172100140435ustar00rootroot00000000000000vuos-0.9.2/vumisc/CMakeLists.txt000066400000000000000000000006311476575172100166030ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(VU_MOD_TARGET vumisc) file(GLOB_RECURSE VUMISC_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUMISC_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") target_link_libraries(${VU_MOD_TARGET} vumod) install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vumisc/vumisc.c000066400000000000000000000256751476575172100155340ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2019 Renzo Davoli VirtualSquare team. * * 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, see . * * vumisc: miscellaneous virtualization. * set single system call behavior, an info file system permits configuration. * e.g.: * vu_insmod vumisc * vumount -t vumisctime none /tmp/time * pseudo file in /tmp/time change the user processes' perception of time. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VUMISC_SC_LIST \ VUMISC(clock_gettime), \ VUMISC(clock_settime), \ VUMISC(clock_getres) /* const char *vumisc_names[] = {"clock_gettime", ....}; */ #define VUMISC(X) #X const char * vumisc_names[] = { VUMISC_SC_LIST }; #undef VUMISC /* const int vumisc_nr[] = {__NR_clock_gettime, ....}; */ #define VUMISC(X) __NR_ ## X const uint16_t vumisc_nr[] = { VUMISC_SC_LIST }; #undef VUMISC /* enum vumisc_index[] = {VUMISC_clock_gettime, ....}; */ #define VUMISC(X) VUMISC_ ## X enum vumisc_index { VUMISC_SC_LIST, NUM_VUMISC_SC }; #define IS_CLOCK_OP(X) \ ((X) >= VUMISC(clock_gettime) && \ (X) <= VUMISC(clock_getres)) VU_PROTOTYPES(vumisc) struct vu_module_t vu_module = { .name = "vumisc", .description = "system call virtualization using info file system" }; struct vumisc_t { void *dlhandle; pthread_mutex_t mutex; struct vumisc_operations_t *misc_ops; void *private_data; struct vuht_entry_t *path_ht; struct vuht_entry_t *ops_ht; syscall_t ops[NUM_VUMISC_SC]; }; static struct vumisc_info *infofs_getinfo(struct vumisc_info *infotree, const char *pathname) { struct vumisc_info *scan; for (scan = infotree; scan->path != NULL; scan++) { if (strcmp(pathname, scan->path) == 0) break; } return scan; } static ino_t ino_hash(const char* path) { ino_t hash = 2; while (*path) hash = 17 * hash ^ (unsigned char) *path++; return hash; } static int vumisc_dir(int tag, FILE *f, int openflags, void *pseudoprivate) { struct vumisc_t *vumisc = vu_get_ht_private_data(); char *prefix = pseudoprivate; size_t prefixlen = strlen(prefix); if (tag == PSEUDOFILE_LOAD_DIRENTS) { struct vumisc_info *scan; pseudofile_filldir(f, ".", 2, DT_DIR); pseudofile_filldir(f, "..", 2, DT_DIR); for (scan = vumisc->misc_ops->infotree; scan->path != NULL; scan++) { if (strncmp(prefix, scan->path, prefixlen) == 0 && scan->path[prefixlen] == '/' && scan->path[prefixlen + 1] != 0 && strchr(scan->path + (prefixlen + 1), '/') == NULL) { ino_t ino = scan->stat.st_ino; if (ino == 0) ino = ino_hash(scan->path); pseudofile_filldir(f, scan->path + (prefixlen + 1), ino, pseudofile_mode2type(scan->stat.st_mode)); } } } return 0; } static int simple_check_permission(int flags, mode_t mode) { switch (flags & O_ACCMODE) { case O_RDONLY : return (mode & S_IRUSR); case O_WRONLY : return (mode & S_IWUSR); case O_RDWR : return (mode & S_IRUSR) && (mode & S_IWUSR); } return 0; } void *vumisc_get_private_data(void) { struct vumisc_t *vumisc = vu_get_ht_private_data(); return vumisc->private_data; } int vu_vumisc_open(const char *pathname, int flags, mode_t mode, void **fdprivate) { struct vumisc_t *vumisc = vu_get_ht_private_data(); struct vumisc_info *scan = infofs_getinfo(vumisc->misc_ops->infotree, pathname); if (scan->path != NULL) { if (simple_check_permission(flags, scan->stat.st_mode) == 0) return errno = EACCES, -1; switch (scan->stat.st_mode & S_IFMT) { case S_IFREG: pseudofile_open(vumisc->misc_ops->infocontents, scan->upcall_private, flags, fdprivate); break; case S_IFDIR: { char *dirpath = (char *)pathname; if (strcmp(dirpath, "/") == 0) dirpath = ""; pseudofile_open(vumisc_dir, dirpath, flags, fdprivate); } break; default: return errno = -EOPNOTSUPP, -1; } return 0; } else return errno = ENOENT, -1; } int vu_vumisc_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *private) { struct vumisc_t *vumisc = vu_get_ht_private_data(); struct vumisc_info *scan = infofs_getinfo(vumisc->misc_ops->infotree, pathname); struct vu_stat *statbuf = &scan->stat; if (statbuf->st_mode != 0) { *buf = *statbuf; if (buf->st_ino == 0) buf->st_ino = ino_hash(pathname); return 0; } else return errno = ENOENT, -1; } #if 0 int vu_vumisc_access(char *path, int mode, int flags) { return 0; } #endif ssize_t vu_vumisc_readlink(char *path, char *buf, size_t bufsiz) { struct vumisc_t *vumisc = vu_get_ht_private_data(); struct vumisc_info *scan = infofs_getinfo(vumisc->misc_ops->infotree, path); if (S_ISLNK(scan->stat.st_mode)) return pseudofile_readlink_fill(scan->upcall_private, buf, bufsiz); else return errno = EINVAL, -1; } int vu_vumisc_unlink(const char *pathname) { return 0; } int vu_vumisc_clock_gettime(clockid_t clk_id, struct timespec *tp) { if (tp == NULL) return errno = -EFAULT, -1; else { struct vumisc_t *vumisc = vu_get_ht_private_data(); return vumisc->ops[VUMISC(clock_gettime)](clk_id, tp); } } int vu_vumisc_clock_settime(clockid_t clk_id, const struct timespec *tp) { if (tp == NULL) return errno = -EFAULT, -1; else { struct vumisc_t *vumisc = vu_get_ht_private_data(); return vumisc->ops[VUMISC(clock_settime)](clk_id, tp); } } int vu_vumisc_clock_getres(clockid_t clk_id, struct timespec *res) { if (res == NULL) return errno = -EFAULT, -1; else { struct vumisc_t *vumisc = vu_get_ht_private_data(); return vumisc->ops[VUMISC(clock_getres)](clk_id, res); } } static int vumisc_confirm(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { int *sc = arg; struct vumisc_t *vumisc = vuht_get_private_data(ht); int index; for (index = 0; index < NUM_VUMISC_SC; index++) { if (vu_arch_table[vumisc_nr[index]] == *sc) break; } if (index == NUM_VUMISC_SC || vumisc->ops[index] == NULL) return 0; else { if (IS_CLOCK_OP(index)) { /* clock_* syscall may support some clk_id only. refusal must be done at ht confirmation time to permit further virtualization by other modules/submodules */ int syscall_number = vu_mod_getsyscall_number(); /* the following applies to __NR_clock_gettime, __NR_clock_settime, __NR_clock_getres, not other calls unified to these like gettimofday/settimeofday */ if (syscall_number == vumisc_nr[index]) { struct vumisc_t *vumisc = vuht_get_private_data(ht); struct vuht_entry_t *oldht = vu_mod_getht(); int ret_value; vu_mod_setht(ht); clockid_t clk_id = vu_mod_getsyscall_arg(0); ret_value = vumisc->ops[index](clk_id, NULL); vu_mod_setht(oldht); if (ret_value < 0) return 0; } } return 1; } } static syscall_t vumisc_getsym(void *handle, const char *filesystemtype, const char *symbol) { size_t fullnamelen = strlen(filesystemtype) + strlen(symbol) + 2; char fullname[fullnamelen]; snprintf(fullname, fullnamelen, "%s_%s", filesystemtype, symbol); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" return dlsym(handle, fullname); #pragma GCC diagnostic pop } int vu_vumisc_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { void *dlhandle = vu_mod_dlopen(filesystemtype, RTLD_NOW); struct vumisc_operations_t *misc_ops; if (data == NULL) data = ""; printkdebug(M,"MOUNT source:%s target:%s type:%s flags:0x%x data:%s", source, target, filesystemtype, mountflags, data); if(dlhandle == NULL || (misc_ops = dlsym(dlhandle,"vumisc_ops")) == NULL) { if (dlhandle != NULL) { printk(KERN_ERR "%s",dlerror()); dlclose(dlhandle); } errno = ENOSYS; return -1; } else { struct vu_service_t *s = vu_mod_getservice(); struct vumisc_t *new = malloc(sizeof(struct vumisc_t)); int i; if (new == NULL) goto err_nomem_misc; new->dlhandle = dlhandle; new->misc_ops = misc_ops; for (i = 0; i < NUM_VUMISC_SC; i++) new->ops[i] = vumisc_getsym(dlhandle, filesystemtype, vumisc_names[i]); pthread_mutex_init(&(new->mutex), NULL); if (misc_ops->init) { new->private_data = misc_ops->init(source); if (new->private_data == NULL) goto err_init_null; } pthread_mutex_lock(&(new->mutex)); new->path_ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, new); new->ops_ht = vuht_add(CHECKSC, NULL, 0, s, 0, vumisc_confirm, new); pthread_mutex_unlock(&(new->mutex)); return errno = 0, 0; err_init_null: pthread_mutex_unlock(&(new->mutex)); free(new); dlclose(dlhandle); return errno = EINVAL, -1; err_nomem_misc: dlclose(dlhandle); return errno = ENOMEM, -1; } } int vu_vumisc_umount2(const char *target, int flags) { struct vumisc_t *vumisc = vu_get_ht_private_data(); pthread_mutex_lock(&(vumisc->mutex)); if (vumisc->path_ht != NULL) vuht_del(vumisc->path_ht, flags); if (vumisc->ops_ht != NULL) vuht_del(vumisc->ops_ht, flags); pthread_mutex_unlock(&(vumisc->mutex)); printkdebug(M,"UMOUNT target:%s flags:%d %p", target, flags, vumisc); return 0; } void vu_vumisc_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vumisc_t *vumisc = vu_get_ht_private_data(); switch (type) { case CHECKPATH: vumisc->path_ht = NULL; break; case CHECKSC: vumisc->ops_ht = NULL; } if (vumisc->path_ht == NULL && vumisc->ops_ht == NULL) { printkdebug(M,"CLEANUP %p", vumisc); if(vumisc->misc_ops->fini) vumisc->misc_ops->fini(vumisc->private_data); pthread_mutex_destroy(&(vumisc->mutex)); dlclose(vumisc->dlhandle); free(vumisc); } } void *vu_vumisc_init(void) { struct vu_service_t *s = vu_mod_getservice(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" vu_syscall_handler(s, close) = pseudofile_close; vu_syscall_handler(s, read) = pseudofile_read; vu_syscall_handler(s, write) = pseudofile_write; vu_syscall_handler(s, lseek) = pseudofile_lseek; vu_syscall_handler(s, getdents64) = pseudofile_getdents64; #pragma GCC diagnostic pop return NULL; } int vu_vumisc_fini(void *private) { return 0; } __attribute__((constructor)) static void init(void) { debug_set_name(M, "VUMISC"); } __attribute__((destructor)) static void fini(void) { debug_set_name(M, ""); } vuos-0.9.2/vumisc_modules/000077500000000000000000000000001476575172100155735ustar00rootroot00000000000000vuos-0.9.2/vumisc_modules/CMakeLists.txt000066400000000000000000000012221476575172100203300ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${VU_DYN_HEADER_PATH}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VUMISC_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) foreach(VUMISC_MOD ${VUMISC_MODULES}) string(REGEX REPLACE "\.c$" "" VUMISC_MOD_FILE ${VUMISC_MOD}) get_filename_component(VUMISC_MOD_TARGET ${VUMISC_MOD_FILE} NAME) add_library(${VUMISC_MOD_TARGET} SHARED ${VUMISC_MOD}) set_target_properties(${VUMISC_MOD_TARGET} PROPERTIES PREFIX "") install(TARGETS ${VUMISC_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) endforeach(VUMISC_MOD) # target_link_libraries(vumiscmod modlib) vuos-0.9.2/vumisc_modules/vumisctime.c000066400000000000000000000120611476575172100201240ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * with contributions by Alessio Volpe * VirtualSquare team. * * 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, see . * * Time virtualization: * e.g. * vu_insmod vumisc * vumount -t vumisctime none /tmp/mnt * ls /tmp/mount * base frequency offset * * if t is the time "below" this virtualization * processes will se the time T * T = (t - base) * freq + base + offset * * base, frequency and offset can be changed by writing the "files" * at the mountpoint (/tmp/mnt in the example) * when frequency is changed, the new base is the "time below" atthe time of change * and offset is changed to preserve continuity. */ #include #include #include #include #include #include #include #include VUMISC_PROTOTYPES(vumisctime) struct vumisctime_t { long double offset; long double base; double freq; }; static struct vumisc_info infotree[] = { {"/", {.st_mode = S_IFDIR | 0777}, ""}, {"/frequency", {.st_mode = S_IFREG | 0666}, "f"}, {"/offset", {.st_mode = S_IFREG | 0666}, "o"}, {"/base", {.st_mode = S_IFREG | 0666}, "b"}, {NULL, {.st_mode = 0}, NULL}}; static long double get_virttime(struct vumisctime_t *vumisct) { struct timespec ts; long double now; clock_gettime(CLOCK_REALTIME,&ts); now = ts.tv_sec + ((long double) ts.tv_nsec) / 1000000000; //printk("get_virttime now %Lf\n",now); now = (now - vumisct->base) * vumisct->freq + vumisct->base + vumisct->offset; //printk("get_virttime umnow %Lf\n",now); return now; } static void set_virttime(struct vumisctime_t *vumisct,long double newnow) { long double now = get_virttime(vumisct); vumisct->offset += newnow - now; } static void set_newfreq(struct vumisctime_t *vumisct,long double newfreq) { struct timespec ts; long double now; long double oldnow; long double newnow; clock_gettime(CLOCK_REALTIME,&ts); now = ts.tv_sec + ((long double) ts.tv_nsec) / 1000000000; oldnow = (now - vumisct->base) * vumisct->freq + vumisct->base; vumisct->base = now; vumisct->freq = newfreq; newnow = (now - vumisct->base) * vumisct->freq + vumisct->base; vumisct->offset += oldnow - newnow; } int infocontents(int tag, FILE *f, int openflags, void *pseudoprivate) { struct vumisctime_t *vumisctime_data = vumisc_get_private_data(); char *filetag = pseudoprivate; if (tag == PSEUDOFILE_LOAD_CONTENTS) { switch (filetag[0]) { case 'f': fprintf(f, "%lf\n", vumisctime_data->freq); break; case 'o': fprintf(f, "%Lf\n", vumisctime_data->offset); break; case 'b': fprintf(f, "%Lf\n", vumisctime_data->base); break; } } if (tag == PSEUDOFILE_STORE_CLOSE && (openflags & O_ACCMODE) != O_RDONLY && f != NULL) { switch (filetag[0]) { case 'f': { double newfreq; if (fscanf(f, "%lf\n", &newfreq) > 0) set_newfreq(vumisctime_data, newfreq); } break; case 'o': { long double offset; if (fscanf(f, "%Lf\n", &offset) > 0) vumisctime_data->offset = offset; } break; case 'b': { long double base; if (fscanf(f, "%Lf\n", &base) > 0) vumisctime_data->base = base; } break; } } return 0; } int vumisctime_clock_gettime(clockid_t clk_id, struct timespec *tp) { struct vumisctime_t *vumisctime_data = vumisc_get_private_data(); if (clk_id == CLOCK_REALTIME) { if (tp) { long double now=get_virttime(vumisctime_data); tp->tv_sec = (time_t) now; tp->tv_nsec = (time_t) ((now - tp->tv_sec) * 1000000000); } return 0; } else return errno = ENOTSUP, -1; } int vumisctime_clock_settime(clockid_t clk_id, const struct timespec *tp) { struct vumisctime_t *vumisctime_data = vumisc_get_private_data(); if (clk_id == CLOCK_REALTIME) { if (tp) { long double newnow; newnow = tp->tv_sec + ((long double) tp->tv_nsec) / 1000000000; set_virttime(vumisctime_data, newnow); } return 0; } else return errno = ENOTSUP, -1; } static void *vumisctime_init(const char *source) { struct vumisctime_t *new = malloc(sizeof(struct vumisctime_t)); if (new) { new->freq = 1.0; new->offset = 0.0; new->base = 0.0; } return new; } static int vumisctime_fini(void *private) { free(private); return 0; } struct vumisc_operations_t vumisc_ops = { .infotree = infotree, .infocontents = infocontents, .init = vumisctime_init, .fini = vumisctime_fini, }; vuos-0.9.2/vunet/000077500000000000000000000000001476575172100136765ustar00rootroot00000000000000vuos-0.9.2/vunet/CMakeLists.txt000066400000000000000000000005471476575172100164440ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS}) set(VU_MOD_TARGET vunet) file(GLOB_RECURSE VUNET_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) add_library(${VU_MOD_TARGET} SHARED ${VUNET_SOURCES}) set_target_properties(${VU_MOD_TARGET} PROPERTIES PREFIX "") install(TARGETS ${VU_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) vuos-0.9.2/vunet/vunet.c000066400000000000000000000414071476575172100152110ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include VU_PROTOTYPES(vunet) struct vu_module_t vu_module = { .name = "vunet", .description = "vu virtual networking" }; struct vunet_default_t { pthread_rwlock_t lock; size_t count; struct vunet *defstack[AF_MAX + 1]; }; static __thread struct vunet_default_t *vunet_default = NULL; static void vu_default_modify_lock(void); static void vu_default_read_lock(void); static void vu_default_unlock(void); struct vunet { void *dlhandle; struct vunet_operations *netops; mode_t mode; uid_t uid; gid_t gid; time_t mounttime; time_t atime; struct vuht_entry_t *socket_ht; struct vuht_entry_t *ioctl_ht; struct vuht_entry_t *path_ht; void *private_data; }; struct vunetfd { struct vunet *vunet; void *netfdprivate; }; static __thread struct vunetfd *current_vnetfd = NULL; void *vunet_get_fdprivate(void) { if (current_vnetfd == NULL) return NULL; else return current_vnetfd->netfdprivate; } void vunet_set_fdprivate(void *fdprivate) { if (current_vnetfd != NULL) current_vnetfd->netfdprivate = fdprivate; } void *vunet_get_private_data(void) { struct vunet *vunet = vu_get_ht_private_data(); if (vunet == NULL) return NULL; else return vunet->private_data; } static struct vunet *get_defstack(int domain) { if (domain >= 0 && domain <= AF_MAX) { if (vunet_default != NULL) { struct vunet *vunet; vu_default_read_lock(); vunet = vunet_default->defstack[domain]; vu_default_unlock(); return vunet; } else return NULL; } else return NULL; } static void set_defstack(int domain, struct vunet *vunet) { if (domain >= 0 && domain <= AF_MAX) { vu_default_modify_lock(); if (domain == PF_UNSPEC) { for (domain = 0; domain <= AF_MAX; domain++) { if (vunet->netops->supported_domain == NULL || vunet->netops->supported_domain(domain)) vunet_default->defstack[domain] = vunet; } } else vunet_default->defstack[domain] = vunet; vu_default_unlock(); } } /* confirmation function for sockets */ static int checksocket(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vunet *vunet = vuht_get_private_data(ht); int *domain = arg; struct vunet *defvunet = get_defstack(*domain); /* if the default stack for this process is this, then ok else skip */ if (vunet == defvunet) { return 1; } else { return 0; } } /* confirmation function for ioctl */ static int checkioctl(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { unsigned long *request = arg; struct vunet *vunet = vuht_get_private_data(ht); struct vunet *defvunet = get_defstack(PF_NETLINK); /* if the default stack for this process is this AND the request is supported, then ok else skip */ if (vunet->netops->supported_ioctl != NULL && vunet->netops->supported_ioctl(*request) && vunet == defvunet) return 1; else return 0; } int vu_vunet_lstat(char *pathname, struct vu_stat *buf, int flags, int sfd, void *fdprivate) { struct vunet *vunet = vu_get_ht_private_data(); memset(buf, 0, sizeof(struct vu_stat)); buf->st_mode = vunet->mode; buf->st_uid = vunet->uid; buf->st_gid = vunet->gid; buf->st_mtime = vunet->mounttime; buf->st_atime = vunet->atime; return 0; } #if 0 int vu_vunet_access(char *path, int mode, int flags) { /* access control */ return 0; } #endif int vu_vunet_chmod(const char *pathname, mode_t mode, int fd, void *fdprivate) { struct vunet *vunet = vu_get_ht_private_data(); /* access control */ vunet->mode = (vunet->mode & S_IFMT) | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); return 0; } int vu_vunet_lchown(const char *pathname, uid_t owner, gid_t group, int fd, void *fdprivate) { struct vunet *vunet = vu_get_ht_private_data(); /* access control */ if (owner != (uid_t) -1) vunet->uid = owner; if (group != (gid_t) -1) vunet->gid = group; return 0; } int vu_vunet_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event, void *fdprivate) { current_vnetfd = fdprivate; if (current_vnetfd->vunet->netops->epoll_ctl == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->epoll_ctl(epfd, op, fd, event); } int vu_vunet_socket(int domain, int type, int protocol, void **fdprivate) { struct vunet *vunet = vu_get_ht_private_data(); if (vunet == NULL) vunet = get_defstack(domain); if (vunet == NULL) return errno = EAFNOSUPPORT, -1; printkdebug(N, "socket stack %p domain 0x%x type 0x%x protocol 0x%x", vunet, domain, type, protocol); if (type == SOCK_DEFAULT) { set_defstack(domain, vunet); return 0; } else { if (vunet->netops->socket == NULL) return errno = ENOSYS, -1; else if (vunet->netops->supported_domain != NULL && ! vunet->netops->supported_domain(domain)) return errno = EAFNOSUPPORT, -1; else { current_vnetfd = malloc(sizeof(struct vunetfd)); if (current_vnetfd == NULL) return errno = ENOMEM, -1; else { int retval; current_vnetfd->vunet = vunet; current_vnetfd->netfdprivate = NULL; retval = vunet->netops->socket(domain, type, protocol); if (retval >= 0) { printkdebug(N, "socket stack %p domain 0x%x type 0x%x protocol 0x%x -> %p", vunet, domain, type, protocol, current_vnetfd); *fdprivate = current_vnetfd; } else { free(current_vnetfd); current_vnetfd = NULL; } return retval; } } } } int vu_vunet_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "bind %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->bind == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->bind(sockfd, addr, addrlen); } int vu_vunet_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "connect %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->connect == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->connect(sockfd, addr, addrlen); } int vu_vunet_listen(int sockfd, int backlog, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "listen %p %d %d", sockfd, current_vnetfd, backlog); if (current_vnetfd->vunet->netops->listen == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->listen(sockfd, backlog); } int vu_vunet_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "accept4 %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->accept4 == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->accept4(sockfd, addr, addrlen, flags); } int vu_vunet_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "getsockname %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->getsockname == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->getsockname(sockfd, addr, addrlen); } int vu_vunet_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "getpeername %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->getpeername == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->getpeername(sockfd, addr, addrlen); } ssize_t vu_vunet_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen, void *msg_control, size_t msg_controllen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "sendto %p %d %p %d", current_vnetfd, sockfd, buf, len); if (current_vnetfd->vunet->netops->sendmsg == NULL) return errno = ENOSYS, -1; else { struct iovec iov[] = {{(void *) buf, len}}; struct msghdr msgh = {(void *) dest_addr, addrlen, iov, 1, msg_control, msg_controllen, 0}; return current_vnetfd->vunet->netops->sendmsg(sockfd, &msgh, flags); } } ssize_t vu_vunet_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen, void *msg_control, size_t *msg_controllen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "recvfrom %p %d %p %d", current_vnetfd, sockfd, buf, len); if (current_vnetfd->vunet->netops->recvmsg == NULL) return errno = ENOSYS, -1; else { struct iovec iov[] = {{buf, len}}; struct msghdr msgh = {(void *) src_addr, *addrlen, iov, 1, msg_control, 0, 0}; if (msg_controllen != NULL) msgh.msg_controllen = *msg_controllen; int retval = current_vnetfd->vunet->netops->recvmsg(sockfd, &msgh, flags); if (retval >= 0) { if (addrlen != NULL) *addrlen = msgh.msg_namelen; if (msg_controllen != NULL) *msg_controllen = msgh.msg_controllen; } return retval; } } int vu_vunet_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "getsockopt %p %d %d %d", current_vnetfd, sockfd, level, optname); if (current_vnetfd->vunet->netops->getsockopt == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->getsockopt(sockfd, level, optname, optval, optlen); } int vu_vunet_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "setsockopt %p %d %d %d", current_vnetfd, sockfd, level, optname); if (current_vnetfd->vunet->netops->setsockopt == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->setsockopt(sockfd, level, optname, optval, optlen); } int vu_vunet_ioctl(int sockfd, unsigned long request, void *buf, uintptr_t addr, void *fdprivate) { struct vunet_operations *netops; current_vnetfd = fdprivate; printkdebug(N, "ioctl %p %d 0x%x %p %d", current_vnetfd, sockfd, request, buf, addr); if (current_vnetfd == NULL) { struct vunet *vunet = vu_get_ht_private_data(); netops = vunet->netops; } else netops = current_vnetfd->vunet->netops; if (netops->ioctl == NULL) return errno = ENOSYS, -1; else { if (request == SIOCGIFCONF && buf != NULL) { struct ifconf *ifc = buf; char *userbuf = ifc->ifc_buf; int ret_value; if (userbuf != NULL) ifc->ifc_buf = malloc(ifc->ifc_len); ret_value = netops->ioctl(sockfd, request, buf); if (ifc->ifc_buf != NULL) { if (ret_value >= 0) vu_mod_poke_data(userbuf, ifc->ifc_buf, ifc->ifc_len); free(ifc->ifc_buf); } ifc->ifc_buf = userbuf; return ret_value; } else return netops->ioctl(sockfd, request, buf); } } int vu_vunet_close(int sockfd, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "close %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->close == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->close(sockfd); } int vu_vunet_fcntl(int sockfd, int cmd, long arg, void *fdprivate) { current_vnetfd = fdprivate; printkdebug(N, "fcntl %p %d", current_vnetfd, sockfd); if (current_vnetfd->vunet->netops->fcntl == NULL) return errno = ENOSYS, -1; else return current_vnetfd->vunet->netops->fcntl(sockfd, cmd, arg); } int vu_vunet_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { void *dlhandle = vu_mod_dlopen(filesystemtype, RTLD_NOW); struct vunet_operations *netops; printkdebug(N, "mount \'%s\' \'%s\' %s", source, target, filesystemtype); if (dlhandle == NULL || (netops = dlsym(dlhandle, "vunet_ops")) == NULL) { if (dlhandle != NULL) dlclose(dlhandle); return errno = ENODEV, -1; } else { struct vu_service_t *s = vu_mod_getservice(); struct vunet *vunet = malloc(sizeof(struct vunet)); int retvalue = 0; if (vunet == NULL) { return errno = ENOMEM, -1; } else { vunet->dlhandle = dlhandle; vunet->netops = netops; vunet->mode = S_IFSTACK | 0777; vunet->uid = 0; vunet->gid = 0; vunet->mounttime = vunet->atime = time(NULL); vunet->private_data = NULL; errno = 0; if (vunet->netops->init != NULL) retvalue = vunet->netops->init(source, mountflags, data, &vunet->private_data); if (retvalue < 0) { free(vunet); if (errno == 0) errno = EINVAL; retvalue = -1; } else { if (vunet->netops->supported_ioctl) vunet->ioctl_ht = vuht_add(CHECKIOCTL, NULL, 0, s, 0, checkioctl, vunet); else vunet->ioctl_ht = NULL; vunet->socket_ht = vuht_add(CHECKSOCKET, NULL, 0, s, 0, checksocket, vunet); vunet->path_ht = vuht_pathadd(CHECKPATH, source, target, filesystemtype, mountflags, data, s, 0, NULL, vunet); printkdebug(N, "mount \'%s\' \'%s\' %s -> %p", source, target, filesystemtype, vunet); } } return retvalue;; } } /* XXX umount usage count (default defs) */ int vu_vunet_umount2(const char *target, int flags) { struct vuht_entry_t *ht = vu_mod_getht(); struct vunet *vunet = vu_get_ht_private_data(); int ret_value; printkdebug(N, "umount2 \'%s\' %p", target, vunet); vuht_del(vunet->socket_ht, flags); if (vunet->ioctl_ht) vuht_del(vunet->ioctl_ht, flags); if ((ret_value = vuht_del(ht, flags)) < 0) return errno = -ret_value, -1; return 0; } void vu_vunet_cleanup(uint8_t type, void *arg, int arglen, struct vuht_entry_t *ht) { struct vunet *vunet = vuht_get_private_data(ht); printkdebug(N, "cleanup %p %d", vunet, type); if (type == CHECKSOCKET) vunet->socket_ht = NULL; if (type == CHECKIOCTL) vunet->ioctl_ht = NULL; if (type == CHECKPATH) vunet->path_ht = NULL; if (vunet->socket_ht == NULL && vunet->ioctl_ht == NULL && vunet->path_ht == NULL) { if (vunet->netops->fini != NULL) vunet->netops->fini(vunet->private_data); free(vunet); } } static void vu_default_modify_lock(void) { if (vunet_default == NULL) { struct vunet_default_t *new; int i; new = malloc(sizeof(struct vunet_default_t)); pthread_rwlock_init(&new->lock, NULL); new->count = 1; for (i = 0; i < AF_MAX + 1; i++) new->defstack[i] = NULL; vunet_default = new; } pthread_rwlock_wrlock(&vunet_default->lock); if (vunet_default->count > 1) { struct vunet_default_t *new; int i; new = malloc(sizeof(struct vunet_default_t)); pthread_rwlock_init(&new->lock, NULL); new->count = 1; vunet_default->count -= 1; for (i = 0; i < AF_MAX + 1; i++) new->defstack[i] = vunet_default->defstack[i]; pthread_rwlock_unlock(&vunet_default->lock); vunet_default = new; pthread_rwlock_wrlock(&vunet_default->lock); } } static void vu_default_read_lock(void) { pthread_rwlock_rdlock(&vunet_default->lock); } static void vu_default_unlock(void) { pthread_rwlock_unlock(&vunet_default->lock); } static void *vunet_default_clone(void *arg) { if (vunet_default != NULL) { pthread_rwlock_wrlock(&vunet_default->lock); vunet_default->count++; pthread_rwlock_unlock(&vunet_default->lock); return vunet_default; } else return NULL; } static void vunet_default_terminate(void) { if (vunet_default != NULL) { pthread_rwlock_wrlock(&vunet_default->lock); vunet_default->count -= 1; if (vunet_default->count == 0) { struct vunet_default_t *old_vunet_default = vunet_default; vunet_default = NULL; pthread_rwlock_unlock(&old_vunet_default->lock); pthread_rwlock_destroy(&old_vunet_default->lock); free(old_vunet_default); } else pthread_rwlock_unlock(&vunet_default->lock); } } static void *vunet_tracer_upcall(mod_inheritance_state_t state, void *ioarg, void *arg) { void *ret_value = NULL; switch (state) { case MOD_INH_CLONE: ret_value = vunet_default_clone(arg); break; case MOD_INH_START: vunet_default = ioarg; break; case MOD_INH_EXEC: break; case MOD_INH_TERMINATE: vunet_default_terminate(); break; } return ret_value; } void *vu_vunet_init (void) { mod_inheritance_upcall_register(vunet_tracer_upcall); return NULL; } int vu_vunet_fini(void *private) { mod_inheritance_upcall_deregister(vunet_tracer_upcall); return 0; } __attribute__((constructor)) static void init(void) { debug_set_name(N, "VUNET"); } __attribute__((destructor)) static void fini(void) { debug_set_name(N, ""); } vuos-0.9.2/vunet/vunet_ioctl.c000066400000000000000000000051051476575172100163760ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include long vunet_ioctl_parms(unsigned long request) { switch (request) { case FIONREAD: return _IOW(' ', 0, int); case FIONBIO: return _IOR(' ', 0, int); case SIOCGIFCONF: return _IOWR(' ', 0, struct ifconf); #ifdef SIOCGSTAMP case SIOCGSTAMP: return _IOW(' ', 0, struct timeval); #endif case SIOCGIFNAME: case SIOCGIFFLAGS: case SIOCGIFADDR: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: case SIOCGIFNETMASK: case SIOCGIFMETRIC: case SIOCGIFMEM: case SIOCGIFMTU: case SIOCGIFHWADDR: case SIOCGIFINDEX: case SIOCGIFTXQLEN: return _IOWR(' ', 0, struct ifreq); case SIOCSIFNAME: case SIOCSIFFLAGS: case SIOCSIFADDR: case SIOCSIFDSTADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: case SIOCSIFMETRIC: case SIOCSIFMEM: case SIOCSIFMTU: case SIOCSIFHWADDR: case SIOCSIFTXQLEN: case SIOCSIFHWBROADCAST: return _IOR(' ', 0, struct ifreq); case SIOCGIFMAP: return _IOWR(' ', 0, struct ifmap); case SIOCSIFMAP: return _IOR(' ', 0, struct ifmap); default: return 0; } } int vunet_is_netdev_ioctl(unsigned long request) { switch (request) { case SIOCGIFCONF: #ifdef SIOCGSTAMP case SIOCGSTAMP: #endif case SIOCGIFNAME: case SIOCGIFFLAGS: case SIOCGIFADDR: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: case SIOCGIFNETMASK: case SIOCGIFMETRIC: case SIOCGIFMEM: case SIOCGIFMTU: case SIOCGIFHWADDR: case SIOCGIFINDEX: case SIOCGIFTXQLEN: case SIOCSIFNAME: case SIOCSIFFLAGS: case SIOCSIFADDR: case SIOCSIFDSTADDR: case SIOCSIFBRDADDR: case SIOCSIFNETMASK: case SIOCSIFMETRIC: case SIOCSIFMEM: case SIOCSIFMTU: case SIOCSIFHWADDR: case SIOCSIFTXQLEN: case SIOCSIFHWBROADCAST: case SIOCGIFMAP: case SIOCSIFMAP: return 1; default: return 0; } } vuos-0.9.2/vunet_modules/000077500000000000000000000000001476575172100154265ustar00rootroot00000000000000vuos-0.9.2/vunet_modules/CMakeLists.txt000066400000000000000000000012111476575172100201610ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.13) include_directories(${VU_HEADERS} ${VU_DYN_HEADER_PATH}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) file(GLOB VUNET_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) foreach(VUNET_MOD ${VUNET_MODULES}) string(REGEX REPLACE "\.c$" "" VUNET_MOD_FILE ${VUNET_MOD}) get_filename_component(VUNET_MOD_TARGET ${VUNET_MOD_FILE} NAME) add_library(${VUNET_MOD_TARGET} SHARED ${VUNET_MOD}) set_target_properties(${VUNET_MOD_TARGET} PROPERTIES PREFIX "") install(TARGETS ${VUNET_MOD_TARGET} LIBRARY DESTINATION ${MODULES_INSTALL_PATH}) endforeach(VUNET_MOD) target_link_libraries(vunetvdestack vdeplug) vuos-0.9.2/vunet_modules/vunetnull.c000066400000000000000000000006421476575172100176300ustar00rootroot00000000000000#include #include static int supported_domain (int domain) { switch (domain) { case AF_INET: case AF_INET6: case AF_NETLINK: case AF_PACKET: return 1; default: return 0; } } static int null_socket(int domain, int type, int protocol) { errno = EAFNOSUPPORT; return -1; } struct vunet_operations vunet_ops = { .socket = null_socket, .supported_domain = supported_domain, }; vuos-0.9.2/vunet_modules/vunetreal.c000066400000000000000000000013241476575172100175770ustar00rootroot00000000000000#include static int supported_domain (int domain) { switch (domain) { case AF_INET: case AF_INET6: case AF_NETLINK: case AF_PACKET: return 1; default: return 0; } } static int netreal_ioctl (int fd, unsigned long request, void *addr) { return ioctl(fd, request, addr); } struct vunet_operations vunet_ops = { .socket = socket, .bind = bind, .connect = connect, .listen = listen, .accept4 = accept4, .getsockname = getsockname, .getpeername = getpeername, .recvmsg = recvmsg, .sendmsg = sendmsg, .getsockopt = getsockopt, .setsockopt = setsockopt, .shutdown = shutdown, .ioctl = netreal_ioctl, .close = close, .epoll_ctl = epoll_ctl, .supported_domain = supported_domain, }; vuos-0.9.2/vunet_modules/vunetvdestack.c000066400000000000000000000144101476575172100204600ustar00rootroot00000000000000/* * VUOS: view OS project * Copyright (C) 2018 Renzo Davoli * VirtualSquare team. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define APPSIDE 0 #define DAEMONSIDE 1 #define DEFAULT_IF_NAME "vde0" #define ETH_HEADER_SIZE 14 #define CHILD_STACK_SIZE (256 * 1024) struct vdestack { pid_t pid; int cmdpipe[2]; // socketpair for commands; VDECONN *vdeconn; char *child_stack; char ifname[]; }; struct vdecmd { int domain; int type; int protocol; }; struct vdereply { int rval; int err; }; static int open_tap(char *name) { struct ifreq ifr; int fd=-1; if((fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC)) < 0) return -1; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1); if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) { close(fd); return -1; } return fd; } static int childFunc(void *arg) { struct vdestack *stack = arg; int n; char buf[VDE_ETHBUFSIZE]; int tapfd = open_tap(stack->ifname); VDECONN *conn = stack->vdeconn; struct pollfd pfd[] = {{stack->cmdpipe[DAEMONSIDE], POLLIN, 0}, {tapfd, POLLIN, 0}, {vde_datafd(conn), POLLIN, 0}}; if (tapfd < 0) { perror("tapfd"); _exit(1); } pfd[1].fd = tapfd; while (poll(pfd, 3, -1) >= 0) { //printk("poll in %d %d %d\n",pfd[0].revents,pfd[1].revents,pfd[2].revents); if (pfd[0].revents & POLLIN) { struct vdecmd cmd; struct vdereply reply; int n; if ((n = read(stack->cmdpipe[DAEMONSIDE], &cmd, sizeof(cmd))) > 0) { reply.rval = socket(cmd.domain, cmd.type, cmd.protocol); reply.err = errno; if (write(stack->cmdpipe[DAEMONSIDE], &reply, sizeof(reply)) < 0) break; } else break; } if (pfd[1].revents & POLLIN) { n = read(tapfd, buf, VDE_ETHBUFSIZE); if (n <= 0) break; vde_send(conn, buf, n, 0); } if (pfd[2].revents & POLLIN) { n = vde_recv(conn, buf, VDE_ETHBUFSIZE, 0); if (n <= 0) break; if (n >= ETH_HEADER_SIZE && write(tapfd, buf, n) < 0) break; } //printk("poll out\n"); } close(stack->cmdpipe[DAEMONSIDE]); _exit(EXIT_SUCCESS); } struct vdestack *vde_addstack(char *vdenet, char *ifname) { char *ifnameok = ifname ? ifname : DEFAULT_IF_NAME; size_t ifnameoklen = strlen(ifnameok); struct vdestack *stack = malloc(sizeof(*stack) + ifnameoklen + 1); if (stack) { strncpy(stack->ifname, ifnameok, ifnameoklen + 1); stack->child_stack = malloc(CHILD_STACK_SIZE); if (stack->child_stack == NULL) goto err_child_stack; if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, stack->cmdpipe) < 0) goto err_cmdpipe; if ((stack->vdeconn = vde_open(vdenet, "vdestack", NULL)) == NULL) goto err_vdenet; stack->pid = clone(childFunc, stack->child_stack + CHILD_STACK_SIZE, CLONE_FILES | CLONE_NEWUSER | CLONE_NEWNET | SIGCHLD, stack); if (stack->pid == -1) goto err_child; } return stack; err_child: err_vdenet: close(stack->cmdpipe[APPSIDE]); close(stack->cmdpipe[DAEMONSIDE]); err_cmdpipe: free(stack->child_stack); err_child_stack: free(stack); return NULL; } void vde_delstack(struct vdestack *stack) { vde_close(stack->vdeconn); close(stack->cmdpipe[APPSIDE]); waitpid(stack->pid, NULL, 0); free(stack->child_stack); free(stack); } int vde_msocket(struct vdestack *stack, int domain, int type, int protocol) { struct vdecmd cmd = {domain, type, protocol}; struct vdereply reply; if (write(stack->cmdpipe[APPSIDE], &cmd, sizeof(cmd)) < 0 || read(stack->cmdpipe[APPSIDE], &reply, sizeof(reply)) < 0) return errno = EFAULT, -1; if (reply.rval < 0) errno = reply.err; return reply.rval; } static int supported_domain (int domain) { switch (domain) { case AF_INET: case AF_INET6: case AF_NETLINK: case AF_PACKET: return 1; default: return 0; } } static int supported_ioctl (unsigned long request) { return vunet_is_netdev_ioctl(request); } static int vdestack_socket(int domain, int type, int protocol) { struct vdestack *vdestack = vunet_get_private_data(); return vde_msocket(vdestack, domain, type, protocol); } static int vdestack_ioctl (int fd, unsigned long request, void *addr) { if (fd == -1) { if (addr == NULL) { int retval = vunet_ioctl_parms(request); if (retval == 0) { errno = ENOSYS; return -1; } else return retval; } else { int tmpfd = vdestack_socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); int retval; if (tmpfd < 0) return -1; else { retval = ioctl(tmpfd, request, addr); close(tmpfd); return retval; } } } else return ioctl(fd, request, addr); } int vdestack_init(const char *source, unsigned long flags, const char *args, void **private_data) { struct vdestack *vdestack = vde_addstack((char *) source, NULL); if (vdestack != NULL) { *private_data = vdestack; return 0; } else { errno = EINVAL; return -1; } } int vdestack_fini(void *private_data) { vde_delstack(private_data); return 0; } struct vunet_operations vunet_ops = { .socket = vdestack_socket, .bind = bind, .connect = connect, .listen = listen, .accept4 = accept4, .getsockname = getsockname, .getpeername = getpeername, .recvmsg = recvmsg, .sendmsg = sendmsg, .getsockopt = getsockopt, .setsockopt = setsockopt, .shutdown = shutdown, .ioctl = vdestack_ioctl, .close = close, .epoll_ctl = epoll_ctl, .supported_domain = supported_domain, .supported_ioctl = supported_ioctl, .init = vdestack_init, .fini = vdestack_fini, };