From 5ac3f4a119eefa8d240bc5b35c6e447497f59d11 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 14 Feb 2015 11:19:08 -0500 Subject: [PATCH 001/280] Initial commit; backport of GLib autoptr code This module is a new successor to libgsystem. It currently contains a backport of the GLib cleanup macros, but soon more Linux-specific code will be added. --- COPYING | 481 +++++++++++++++++++++++++++++++++++ Makefile-libglnx.am | 28 ++ README | 10 + glnx-backport-autocleanups.h | 71 ++++++ glnx-backport-autoptr.h | 132 ++++++++++ glnx-local-alloc.c | 72 ++++++ glnx-local-alloc.h | 214 ++++++++++++++++ libglnx.doap | 31 +++ libglnx.h | 31 +++ 9 files changed, 1070 insertions(+) create mode 100644 COPYING create mode 100644 Makefile-libglnx.am create mode 100644 README create mode 100644 glnx-backport-autocleanups.h create mode 100644 glnx-backport-autoptr.h create mode 100644 glnx-local-alloc.c create mode 100644 glnx-local-alloc.h create mode 100644 libglnx.doap create mode 100644 libglnx.h diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5bc8fb2c --- /dev/null +++ b/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + 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 Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 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 a program 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. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + 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, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +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 compile 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) 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. + + c) 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. + + d) 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 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. + + 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 to +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 Library 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library 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! diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am new file mode 100644 index 00000000..6340063c --- /dev/null +++ b/Makefile-libglnx.am @@ -0,0 +1,28 @@ +# Copyright (C) 2015 Colin Walters +# +# 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 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., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +EXTRA_DIST += $(libglnx_srcpath)/README $(libglnx_srcpath)/COPYING + +libglnx_la_SOURCES = \ + $(libglnx_srcpath)/glnx-backport-autocleanups.h \ + $(libglnx_srcpath)/glnx-backport-autoptr.h \ + $(libglnx_srcpath)/libglnx.h \ + $(NULL) + +libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic +libglnx_la_LIBADD = $(libglnx_libs) diff --git a/README b/README new file mode 100644 index 00000000..13fa05f2 --- /dev/null +++ b/README @@ -0,0 +1,10 @@ +libgsystem is intended to be used as a git external for components +that depend on GLib, but accept a hard dependency on things which are +difficult to do in GLib itself. For example, the local-alloc API +depends on GCC/clang. + +It will also be a useful place to evolve new APIs (e.g. logging) +before their eventual inclusion in GLib (if ever). + +There's no bugzilla component currently, please mail +patches to: walters@verbum.org diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h new file mode 100644 index 00000000..05ac49ee --- /dev/null +++ b/glnx-backport-autocleanups.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2015 Canonical Limited + * + * 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 of the licence, 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, see . + * + * Author: Ryan Lortie + */ + +#pragma once + +#include + +#if !GLIB_CHECK_VERSION(2, 44, 0) + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GChecksum, g_checksum_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDateTime, g_date_time_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDir, g_dir_close) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GError, g_error_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHashTable, g_hash_table_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionGroup, g_option_group_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPatternSpec, g_pattern_spec_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GQueue, g_queue_free) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRand, g_rand_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRegex, g_regex_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMatchInfo, g_match_info_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTree, g_tree_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) + +#endif diff --git a/glnx-backport-autoptr.h b/glnx-backport-autoptr.h new file mode 100644 index 00000000..de611b7f --- /dev/null +++ b/glnx-backport-autoptr.h @@ -0,0 +1,132 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2, 44, 0) + +#define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName +#define _GLIB_AUTOPTR_TYPENAME(TypeName) TypeName##_autoptr +#define _GLIB_AUTO_FUNC_NAME(TypeName) glib_auto_cleanup_##TypeName +#define _GLIB_CLEANUP(func) __attribute__((cleanup(func))) +#define _GLIB_DEFINE_AUTOPTR_CHAINUP(ModuleObjName, ParentName) \ + typedef ModuleObjName *_GLIB_AUTOPTR_TYPENAME(ModuleObjName); \ + static inline void _GLIB_AUTOPTR_FUNC_NAME(ModuleObjName) (ModuleObjName **_ptr) { \ + _GLIB_AUTOPTR_FUNC_NAME(ParentName) ((ParentName **) _ptr); } \ + + +/* these macros are API */ +#define G_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, func) \ + typedef TypeName *_GLIB_AUTOPTR_TYPENAME(TypeName); \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTOPTR_FUNC_NAME(TypeName) (TypeName **_ptr) { if (*_ptr) (func) (*_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TypeName, func) \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { (func) (_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define G_DEFINE_AUTO_CLEANUP_FREE_FUNC(TypeName, func, none) \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + static inline void _GLIB_AUTO_FUNC_NAME(TypeName) (TypeName *_ptr) { if (*_ptr != none) (func) (*_ptr); } \ + G_GNUC_END_IGNORE_DEPRECATIONS +#define g_autoptr(TypeName) _GLIB_CLEANUP(_GLIB_AUTOPTR_FUNC_NAME(TypeName)) _GLIB_AUTOPTR_TYPENAME(TypeName) +#define g_auto(TypeName) _GLIB_CLEANUP(_GLIB_AUTO_FUNC_NAME(TypeName)) TypeName + +/** + * g_steal_pointer: + * @pp: a pointer to a pointer + * + * Sets @pp to %NULL, returning the value that was there before. + * + * Conceptually, this transfers the ownership of the pointer from the + * referenced variable to the "caller" of the macro (ie: "steals" the + * reference). + * + * The return value will be properly typed, according to the type of + * @pp. + * + * This can be very useful when combined with g_autoptr() to prevent the + * return value of a function from being automatically freed. Consider + * the following example (which only works on GCC and clang): + * + * |[ + * GObject * + * create_object (void) + * { + * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL); + * + * if (early_error_case) + * return NULL; + * + * return g_steal_pointer (&obj); + * } + * ]| + * + * It can also be used in similar ways for 'out' parameters and is + * particularly useful for dealing with optional out parameters: + * + * |[ + * gboolean + * get_object (GObject **obj_out) + * { + * g_autoptr(GObject) obj = g_object_new (G_TYPE_OBJECT, NULL); + * + * if (early_error_case) + * return FALSE; + * + * if (obj_out) + * *obj_out = g_steal_pointer (&obj); + * + * return TRUE; + * } + * ]| + * + * In the above example, the object will be automatically freed in the + * early error case and also in the case that %NULL was given for + * @obj_out. + * + * Since: 2.44 + */ +static inline gpointer +(g_steal_pointer) (gpointer pp) +{ + gpointer *ptr = (gpointer *) pp; + gpointer ref; + + ref = *ptr; + *ptr = NULL; + + return ref; +} + +/* type safety */ +#define g_steal_pointer(pp) \ + (0 ? (*(pp)) : (g_steal_pointer) (pp)) + +#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ + +G_END_DECLS diff --git a/glnx-local-alloc.c b/glnx-local-alloc.c new file mode 100644 index 00000000..692f0de2 --- /dev/null +++ b/glnx-local-alloc.c @@ -0,0 +1,72 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2015 Colin Walters + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-local-alloc.h" + +/** + * SECTION:glnxlocalalloc + * @title: GLnx local allocation + * @short_description: Release local variables automatically when they go out of scope + * + * These macros leverage the GCC extension __attribute__ ((cleanup)) + * to allow calling a cleanup function such as g_free() when a + * variable goes out of scope. See + * for more information on the attribute. + * + * The provided macros make it easy to use the cleanup attribute for + * types that come with GLib. The primary two are #glnx_free and + * #glnx_unref_object, which correspond to g_free() and + * g_object_unref(), respectively. + * + * The rationale behind this is that particularly when handling error + * paths, it can be very tricky to ensure the right variables are + * freed. With this, one simply applies glnx_unref_object to a + * locally-allocated #GFile for example, and it will be automatically + * unreferenced when it goes out of scope. + * + * Note - you should only use these macros for stack + * allocated variables. They don't provide garbage + * collection or let you avoid freeing things. They're simply a + * compiler assisted deterministic mechanism for calling a cleanup + * function when a stack frame ends. + * + * Calling g_free automatically + * + * + * GFile * + * create_file (GError **error) + * { + * glnx_free char *random_id = NULL; + * + * if (!prepare_file (error)) + * return NULL; + * + * random_id = alloc_random_id (); + * + * return create_file_real (error); + * // Note that random_id is freed here automatically + * } + * + * + * + */ diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h new file mode 100644 index 00000000..a193e603 --- /dev/null +++ b/glnx-local-alloc.h @@ -0,0 +1,214 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GLNX_DEFINE_CLEANUP_FUNCTION(Type, name, func) \ + static inline void name (void *v) \ + { \ + func (*(Type*)v); \ + } + +#define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ + static inline void name (void *v) \ + { \ + if (*(Type*)v) \ + func (*(Type*)v); \ + } + +/** + * glnx_free: + * + * Call g_free() on a variable location when it goes out of scope. + */ +#define glnx_free __attribute__ ((cleanup(glnx_local_free))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_free __attribute__ ((cleanup(glnx_local_free))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free) + +/** + * glnx_unref_object: + * + * Call g_object_unref() on a variable location when it goes out of + * scope. Note that unlike g_object_unref(), the variable may be + * %NULL. + */ +#define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref) + +/** + * glnx_unref_variant: + * + * Call g_variant_unref() on a variable location when it goes out of + * scope. Note that unlike g_variant_unref(), the variable may be + * %NULL. + */ +#define glnx_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariant*, glnx_local_variant_unref, g_variant_unref) + +/** + * glnx_free_variant_iter: + * + * Call g_variant_iter_free() on a variable location when it goes out of + * scope. + */ +#define glnx_free_variant_iter __attribute__ ((cleanup(glnx_local_variant_iter_free))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, glnx_local_variant_iter_free, g_variant_iter_free) + +/** + * glnx_free_variant_builder: + * + * Call g_variant_builder_unref() on a variable location when it goes out of + * scope. + */ +#define glnx_unref_variant_builder __attribute__ ((cleanup(glnx_local_variant_builder_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, glnx_local_variant_builder_unref, g_variant_builder_unref) + +/** + * glnx_unref_array: + * + * Call g_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_array_unref(), the variable may be + * %NULL. + + */ +#define glnx_unref_array __attribute__ ((cleanup(glnx_local_array_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GArray*, glnx_local_array_unref, g_array_unref) + +/** + * glnx_unref_ptrarray: + * + * Call g_ptr_array_unref() on a variable location when it goes out of + * scope. Note that unlike g_ptr_array_unref(), the variable may be + * %NULL. + + */ +#define glnx_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, glnx_local_ptrarray_unref, g_ptr_array_unref) + +/** + * glnx_unref_hashtable: + * + * Call g_hash_table_unref() on a variable location when it goes out + * of scope. Note that unlike g_hash_table_unref(), the variable may + * be %NULL. + */ +#define glnx_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) +#ifdef GLNX_GSYSTEM_COMPAT +#define gs_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) +#endif +GLNX_DEFINE_CLEANUP_FUNCTION0(GHashTable*, glnx_local_hashtable_unref, g_hash_table_unref) + +/** + * glnx_free_list: + * + * Call g_list_free() on a variable location when it goes out + * of scope. + */ +#define glnx_free_list __attribute__ ((cleanup(glnx_local_free_list))) +GLNX_DEFINE_CLEANUP_FUNCTION(GList*, glnx_local_free_list, g_list_free) + +/** + * glnx_free_slist: + * + * Call g_slist_free() on a variable location when it goes out + * of scope. + */ +#define glnx_free_slist __attribute__ ((cleanup(glnx_local_free_slist))) +GLNX_DEFINE_CLEANUP_FUNCTION(GSList*, glnx_local_free_slist, g_slist_free) + +/** + * glnx_free_checksum: + * + * Call g_checksum_free() on a variable location when it goes out + * of scope. Note that unlike g_checksum_free(), the variable may + * be %NULL. + */ +#define glnx_free_checksum __attribute__ ((cleanup(glnx_local_checksum_free))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GChecksum*, glnx_local_checksum_free, g_checksum_free) + +/** + * glnx_unref_bytes: + * + * Call g_bytes_unref() on a variable location when it goes out + * of scope. Note that unlike g_bytes_unref(), the variable may + * be %NULL. + */ +#define glnx_unref_bytes __attribute__ ((cleanup(glnx_local_bytes_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GBytes*, glnx_local_bytes_unref, g_bytes_unref) + +/** + * glnx_strfreev: + * + * Call g_strfreev() on a variable location when it goes out of scope. + */ +#define glnx_strfreev __attribute__ ((cleanup(glnx_local_strfreev))) +GLNX_DEFINE_CLEANUP_FUNCTION(char**, glnx_local_strfreev, g_strfreev) + +/** + * glnx_free_error: + * + * Call g_error_free() on a variable location when it goes out of scope. + */ +#define glnx_free_error __attribute__ ((cleanup(glnx_local_free_error))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GError*, glnx_local_free_error, g_error_free) + +/** + * glnx_unref_keyfile: + * + * Call g_key_file_unref() on a variable location when it goes out of scope. + */ +#define glnx_unref_keyfile __attribute__ ((cleanup(glnx_local_keyfile_unref))) +GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_unref) + +static inline void +glnx_cleanup_close_fdp (int *fdp) +{ + int fd; + + g_assert (fdp); + + fd = *fdp; + if (fd != -1) + (void) close (fd); +} + +/** + * glnx_fd_close: + * + * Call close() on a variable location when it goes out of scope. + */ +#define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp))) + +G_END_DECLS diff --git a/libglnx.doap b/libglnx.doap new file mode 100644 index 00000000..2b38074e --- /dev/null +++ b/libglnx.doap @@ -0,0 +1,31 @@ + + + + libglnx + libglnx + + "Copylib" for system service modules using GLib with Linux + + This module is intended for use by + infrastructure code using GLib that is also Linux specific, such as + ostree, NetworkManager, and others. + + + + + + C + + + + Colin Walters + + walters + + + + diff --git a/libglnx.h b/libglnx.h new file mode 100644 index 00000000..12881eb5 --- /dev/null +++ b/libglnx.h @@ -0,0 +1,31 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2013,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#include +#include +#include + +G_END_DECLS From 8e9a171ec4fe7c2da489b5d9d03b3fd5526a86fa Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 09:54:38 -0500 Subject: [PATCH 002/280] Import directory iteration, errno handling, and shutil from libgsystem --- Makefile-libglnx.am | 8 ++ glnx-backport-autocleanups.h | 2 +- glnx-backport-autoptr.h | 4 +- glnx-dirfd.c | 165 ++++++++++++++++++++++++++++++ glnx-dirfd.h | 60 +++++++++++ glnx-errors.c | 53 ++++++++++ glnx-errors.h | 49 +++++++++ glnx-shutil.c | 188 +++++++++++++++++++++++++++++++++++ glnx-shutil.h | 33 ++++++ libglnx.h | 3 + 10 files changed, 562 insertions(+), 3 deletions(-) create mode 100644 glnx-dirfd.c create mode 100644 glnx-dirfd.h create mode 100644 glnx-errors.c create mode 100644 glnx-errors.h create mode 100644 glnx-shutil.c create mode 100644 glnx-shutil.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 6340063c..46200013 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -20,6 +20,14 @@ EXTRA_DIST += $(libglnx_srcpath)/README $(libglnx_srcpath)/COPYING libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ + $(libglnx_srcpath)/glnx-local-alloc.h \ + $(libglnx_srcpath)/glnx-local-alloc.c \ + $(libglnx_srcpath)/glnx-errors.h \ + $(libglnx_srcpath)/glnx-errors.c \ + $(libglnx_srcpath)/glnx-dirfd.h \ + $(libglnx_srcpath)/glnx-dirfd.c \ + $(libglnx_srcpath)/glnx-shutil.h \ + $(libglnx_srcpath)/glnx-shutil.c \ $(libglnx_srcpath)/libglnx.h \ $(NULL) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 05ac49ee..8fd9b9c1 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -21,7 +21,7 @@ #include -#if !GLIB_CHECK_VERSION(2, 44, 0) +#if !GLIB_CHECK_VERSION(2, 43, 3) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) diff --git a/glnx-backport-autoptr.h b/glnx-backport-autoptr.h index de611b7f..fd5f5d01 100644 --- a/glnx-backport-autoptr.h +++ b/glnx-backport-autoptr.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS -#if !GLIB_CHECK_VERSION(2, 44, 0) +#if !GLIB_CHECK_VERSION(2, 43, 4) #define _GLIB_AUTOPTR_FUNC_NAME(TypeName) glib_autoptr_cleanup_##TypeName #define _GLIB_AUTOPTR_TYPENAME(TypeName) TypeName##_autoptr @@ -127,6 +127,6 @@ static inline gpointer #define g_steal_pointer(pp) \ (0 ? (*(pp)) : (g_steal_pointer) (pp)) -#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ +#endif /* !GLIB_CHECK_VERSION(2, 43, 3) */ G_END_DECLS diff --git a/glnx-dirfd.c b/glnx-dirfd.c new file mode 100644 index 00000000..8bc288b0 --- /dev/null +++ b/glnx-dirfd.c @@ -0,0 +1,165 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include +#include + +/** + * glnx_opendirat_with_errno: + * @dfd: File descriptor for origin directory + * @name: Pathname, relative to @dfd + * @follow: Whether or not to follow symbolic links + * + * Use openat() to open a directory, using a standard set of flags. + * This function sets errno. + */ +int +glnx_opendirat_with_errno (int dfd, + const char *path, + gboolean follow) +{ + int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY; + if (!follow) + flags |= O_NOFOLLOW; + return openat (dfd, path, flags); +} + +/** + * glnx_opendirat: + * @dfd: File descriptor for origin directory + * @path: Pathname, relative to @dfd + * @follow: Whether or not to follow symbolic links + * @error: Error + * + * Use openat() to open a directory, using a standard set of flags. + */ +gboolean +glnx_opendirat (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error) +{ + int ret = glnx_opendirat_with_errno (dfd, path, follow); + if (ret == -1) + { + glnx_set_prefix_error_from_errno (error, "%s", "openat"); + return FALSE; + } + *out_fd = ret; + return TRUE; +} + +struct GLnxRealDirfdIterator +{ + gboolean initialized; + int fd; + DIR *d; +}; +typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator; + +gboolean +glnx_dirfd_iterator_init_at (int dfd, + const char *path, + gboolean follow, + GLnxDirFdIterator *dfd_iter, + GError **error) +{ + gboolean ret = FALSE; + glnx_fd_close int fd = -1; + + if (!glnx_opendirat (dfd, path, follow, &fd, error)) + goto out; + + if (!glnx_dirfd_iterator_init_take_fd (fd, dfd_iter, error)) + goto out; + fd = -1; /* Transfer ownership */ + + ret = TRUE; + out: + return ret; +} + +gboolean +glnx_dirfd_iterator_init_take_fd (int dfd, + GLnxDirFdIterator *dfd_iter, + GError **error) +{ + gboolean ret = FALSE; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + DIR *d = NULL; + + d = fdopendir (dfd); + if (!d) + { + glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); + goto out; + } + + real_dfd_iter->fd = dfd; + real_dfd_iter->d = d; + + ret = TRUE; + out: + return ret; +} + +gboolean +glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + do + { + errno = 0; + *out_dent = readdir (real_dfd_iter->d); + if (*out_dent == NULL && errno != 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); + goto out; + } + } while (*out_dent && + (strcmp ((*out_dent)->d_name, ".") == 0 || + strcmp ((*out_dent)->d_name, "..") == 0)); + + ret = TRUE; + out: + return ret; +} + +void +glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) +{ + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + /* fd is owned by dfd_iter */ + (void) closedir (real_dfd_iter->d); +} diff --git a/glnx-dirfd.h b/glnx-dirfd.h new file mode 100644 index 00000000..ccb6aa2f --- /dev/null +++ b/glnx-dirfd.h @@ -0,0 +1,60 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +struct GLnxDirFdIterator { + gboolean initialized; + int fd; + gpointer padding_data[4]; +}; + +typedef struct GLnxDirFdIterator GLnxDirFdIterator; +gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path, + gboolean follow, + GLnxDirFdIterator *dfd_iter, GError **error); +gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error); +gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error); +void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter); + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear) + +int glnx_opendirat_with_errno (int dfd, + const char *path, + gboolean follow); + +gboolean glnx_opendirat (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error); + +G_END_DECLS diff --git a/glnx-errors.c b/glnx-errors.c new file mode 100644 index 00000000..3cbda7e4 --- /dev/null +++ b/glnx-errors.c @@ -0,0 +1,53 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +void +glnx_real_set_prefix_error_from_errno (GError **error, + gint errsv, + const char *format, + ...) +{ + if (!error) + return; + else + { + GString *buf = g_string_new (""); + va_list args; + + va_start (args, format); + g_string_append_vprintf (buf, format, args); + va_end (args); + + g_string_append (buf, ": "); + g_string_append (buf, g_strerror (errsv)); + + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + buf->str); + g_string_free (buf, TRUE); + errno = errsv; + } +} diff --git a/glnx-errors.h b/glnx-errors.h new file mode 100644 index 00000000..e51cd7e8 --- /dev/null +++ b/glnx-errors.h @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define glnx_set_error_from_errno(error) \ + do { \ + int errsv = errno; \ + g_set_error_literal (error, G_IO_ERROR, \ + g_io_error_from_errno (errsv), \ + g_strerror (errsv)); \ + errno = errsv; \ + } while (0); + +#define glnx_set_prefix_error_from_errno(error, format, args...) \ + do { \ + int errsv = errno; \ + glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \ + errno = errsv; \ + } while (0); + +void glnx_real_set_prefix_error_from_errno (GError **error, + gint errsv, + const char *format, + ...) G_GNUC_PRINTF (3,4); + +G_END_DECLS diff --git a/glnx-shutil.c b/glnx-shutil.c new file mode 100644 index 00000000..9fcb65a0 --- /dev/null +++ b/glnx-shutil.c @@ -0,0 +1,188 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include +#include + +static unsigned char +struct_stat_to_dt (struct stat *stbuf) +{ + if (S_ISDIR (stbuf->st_mode)) + return DT_DIR; + if (S_ISREG (stbuf->st_mode)) + return DT_REG; + if (S_ISCHR (stbuf->st_mode)) + return DT_CHR; + if (S_ISBLK (stbuf->st_mode)) + return DT_BLK; + if (S_ISFIFO (stbuf->st_mode)) + return DT_FIFO; + if (S_ISLNK (stbuf->st_mode)) + return DT_LNK; + if (S_ISSOCK (stbuf->st_mode)) + return DT_SOCK; + return DT_UNKNOWN; +} + +static gboolean +glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + struct dirent *dent; + + while (TRUE) + { + if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, cancellable, error)) + goto out; + + if (dent == NULL) + break; + + if (dent->d_type == DT_UNKNOWN) + { + struct stat stbuf; + if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) + { + if (errno == ENOENT) + continue; + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + dent->d_type = struct_stat_to_dt (&stbuf); + /* Assume unknown types are just treated like regular files */ + if (dent->d_type == DT_UNKNOWN) + dent->d_type = DT_REG; + } + + if (dent->d_type == DT_DIR) + { + g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, }; + + if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE, + &child_dfd_iter, error)) + goto out; + + if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error)) + goto out; + + if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_shutil_rm_rf_at: + * @dfd: A directory file descriptor, or -1 for current + * @path: Path + * @cancellable: Cancellable + * @error: Error + * + * Recursively delete the filename referenced by the combination of + * the directory fd @dfd and @path; it may be a file or directory. No + * error is thrown if @path does not exist. + */ +gboolean +glnx_shutil_rm_rf_at (int dfd, + const char *path, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + glnx_fd_close int target_dfd = -1; + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + /* With O_NOFOLLOW first */ + target_dfd = openat (dfd, path, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); + + if (target_dfd == -1) + { + int errsv = errno; + if (errsv == ENOENT) + { + ; + } + else if (errsv == ENOTDIR || errsv == ELOOP) + { + if (unlinkat (dfd, path, 0) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + else + { + if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error)) + goto out; + target_dfd = -1; + + if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error)) + goto out; + + if (unlinkat (dfd, path, AT_REMOVEDIR) == -1) + { + int errsv = errno; + if (errsv != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + + ret = TRUE; + out: + return ret; +} diff --git a/glnx-shutil.h b/glnx-shutil.h new file mode 100644 index 00000000..0c53fd86 --- /dev/null +++ b/glnx-shutil.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +gboolean +glnx_shutil_rm_rf_at (int dfd, + const char *path, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index 12881eb5..294b9044 100644 --- a/libglnx.h +++ b/libglnx.h @@ -27,5 +27,8 @@ G_BEGIN_DECLS #include #include #include +#include +#include +#include G_END_DECLS From ba67dd39a7d5cc779f83bb22512740cdb4c61b33 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 11:57:15 -0500 Subject: [PATCH 003/280] Update README --- README | 10 ---------- README.md | 4 ++++ 2 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 13fa05f2..00000000 --- a/README +++ /dev/null @@ -1,10 +0,0 @@ -libgsystem is intended to be used as a git external for components -that depend on GLib, but accept a hard dependency on things which are -difficult to do in GLib itself. For example, the local-alloc API -depends on GCC/clang. - -It will also be a useful place to evolve new APIs (e.g. logging) -before their eventual inclusion in GLib (if ever). - -There's no bugzilla component currently, please mail -patches to: walters@verbum.org diff --git a/README.md b/README.md new file mode 100644 index 00000000..1767a2d6 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem + +It is for modules which depend on both GLib and Linux, intended to be +used as a git submodule. From f5399c8348b04f9cf5bd4d9d3903e5843c1feeb5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 16:09:58 -0500 Subject: [PATCH 004/280] Import xattr reading code from libgsystem --- Makefile-libglnx.am | 6 +- glnx-xattrs.c | 283 ++++++++++++++++++++++++++++++++++++++++++++ glnx-xattrs.h | 45 +++++++ libglnx.h | 1 + 4 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 glnx-xattrs.c create mode 100644 glnx-xattrs.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 46200013..a04f19e7 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -26,11 +26,13 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-errors.c \ $(libglnx_srcpath)/glnx-dirfd.h \ $(libglnx_srcpath)/glnx-dirfd.c \ + $(libglnx_srcpath)/glnx-xattrs.h \ + $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ $(libglnx_srcpath)/glnx-shutil.c \ $(libglnx_srcpath)/libglnx.h \ $(NULL) libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) -libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic -libglnx_la_LIBADD = $(libglnx_libs) +libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic +libglnx_la_LIBADD = $(libglnx_libs) -lattr diff --git a/glnx-xattrs.c b/glnx-xattrs.c new file mode 100644 index 00000000..249b2115 --- /dev/null +++ b/glnx-xattrs.c @@ -0,0 +1,283 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +static GVariant * +variant_new_ay_bytes (GBytes *bytes) +{ + gsize size; + gconstpointer data; + data = g_bytes_get_data (bytes, &size); + g_bytes_ref (bytes); + return g_variant_new_from_data (G_VARIANT_TYPE ("ay"), data, size, + TRUE, (GDestroyNotify)g_bytes_unref, bytes); +} + +static char * +canonicalize_xattrs (char *xattr_string, + size_t len) +{ + char *p; + GSList *xattrs = NULL; + GSList *iter; + GString *result; + + result = g_string_new (0); + + p = xattr_string; + while (p < xattr_string+len) + { + xattrs = g_slist_prepend (xattrs, p); + p += strlen (p) + 1; + } + + xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp); + for (iter = xattrs; iter; iter = iter->next) { + g_string_append (result, iter->data); + g_string_append_c (result, '\0'); + } + + g_slist_free (xattrs); + return g_string_free (result, FALSE); +} + +static gboolean +read_xattr_name_array (const char *path, + int fd, + const char *xattrs, + size_t len, + GVariantBuilder *builder, + GError **error) +{ + gboolean ret = FALSE; + const char *p; + int r; + const char *funcstr; + + g_assert (path != NULL || fd != -1); + + funcstr = fd != -1 ? "fgetxattr" : "lgetxattr"; + + p = xattrs; + while (p < xattrs+len) + { + ssize_t bytes_read; + char *buf; + GBytes *bytes = NULL; + + if (fd != -1) + bytes_read = fgetxattr (fd, p, NULL, 0); + else + bytes_read = lgetxattr (path, p, NULL, 0); + if (bytes_read < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + goto out; + } + if (bytes_read == 0) + continue; + + buf = g_malloc (bytes_read); + bytes = g_bytes_new_take (buf, bytes_read); + if (fd != -1) + r = fgetxattr (fd, p, buf, bytes_read); + else + r = lgetxattr (path, p, buf, bytes_read); + if (r < 0) + { + g_bytes_unref (bytes); + glnx_set_prefix_error_from_errno (error, "%s", funcstr); + goto out; + } + + g_variant_builder_add (builder, "(@ay@ay)", + g_variant_new_bytestring (p), + variant_new_ay_bytes (bytes)); + + p = p + strlen (p) + 1; + g_bytes_unref (bytes); + } + + ret = TRUE; + out: + return ret; +} + +static gboolean +get_xattrs_impl (const char *path, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = llistxattr (path, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (llistxattr (path, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_fd_get_all_xattrs: + * @fd: a file descriptor + * @out_xattrs: (out): A new #GVariant containing the extended attributes + * @cancellable: Cancellable + * @error: Error + * + * Read all extended attributes from @fd in a canonical sorted order, and + * set @out_xattrs with the result. + * + * If the filesystem does not support extended attributes, @out_xattrs + * will have 0 elements, and this function will return successfully. + */ +gboolean +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + ssize_t bytes_read; + glnx_free char *xattr_names = NULL; + glnx_free char *xattr_names_canonical = NULL; + GVariantBuilder builder; + gboolean builder_initialized = FALSE; + g_autoptr(GVariant) ret_xattrs = NULL; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); + builder_initialized = TRUE; + + bytes_read = flistxattr (fd, NULL, 0); + + if (bytes_read < 0) + { + if (errno != ENOTSUP) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + } + else if (bytes_read > 0) + { + xattr_names = g_malloc (bytes_read); + if (flistxattr (fd, xattr_names, bytes_read) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); + goto out; + } + xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); + + if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error)) + goto out; + } + + ret_xattrs = g_variant_builder_end (&builder); + builder_initialized = FALSE; + g_variant_ref_sink (ret_xattrs); + + ret = TRUE; + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); + out: + if (!builder_initialized) + g_variant_builder_clear (&builder); + return ret; +} + +/** + * glnx_dfd_name_get_all_xattrs: + * @dfd: Parent directory file descriptor + * @name: File name + * @out_xattrs: (out): Extended attribute set + * @cancellable: Cancellable + * @error: Error + * + * Load all extended attributes for the file named @name residing in + * directory @dfd. + */ +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error) +{ + if (dfd == AT_FDCWD || dfd == -1) + { + return get_xattrs_impl (name, out_xattrs, cancellable, error); + } + else + { + char buf[PATH_MAX]; + /* A workaround for the lack of lgetxattrat(), thanks to Florian Weimer: + * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html + */ + snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); + return get_xattrs_impl (buf, out_xattrs, cancellable, error); + } +} diff --git a/glnx-xattrs.h b/glnx-xattrs.h new file mode 100644 index 00000000..360092d4 --- /dev/null +++ b/glnx-xattrs.h @@ -0,0 +1,45 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +gboolean +glnx_dfd_name_get_all_xattrs (int dfd, + const char *name, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_fd_get_all_xattrs (int fd, + GVariant **out_xattrs, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index 294b9044..e74eedab 100644 --- a/libglnx.h +++ b/libglnx.h @@ -30,5 +30,6 @@ G_BEGIN_DECLS #include #include #include +#include G_END_DECLS From a90e1c3423d815931408b39181e10d281ae3d40b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 17:34:53 -0500 Subject: [PATCH 005/280] Import xattr setting code from libgsystem --- glnx-xattrs.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-xattrs.h | 13 ++++++ 2 files changed, 127 insertions(+) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 249b2115..3421cccf 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -281,3 +281,117 @@ glnx_dfd_name_get_all_xattrs (int dfd, return get_xattrs_impl (buf, out_xattrs, cancellable, error); } } + +static gboolean +set_all_xattrs_for_path (const char *path, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int i, n; + + n = g_variant_n_children (xattrs); + for (i = 0; i < n; i++) + { + const guint8* name; + g_autoptr(GVariant) value = NULL; + const guint8* value_data; + gsize value_len; + + g_variant_get_child (xattrs, i, "(^&ay@ay)", + &name, &value); + value_data = g_variant_get_fixed_array (value, &value_len, 1); + + if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr"); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_dfd_name_set_all_xattrs: + * @dfd: Parent directory file descriptor + * @name: File name + * @xattrs: Extended attribute set + * @cancellable: Cancellable + * @error: Error + * + * Set all extended attributes for the file named @name residing in + * directory @dfd. + */ +gboolean +glnx_dfd_name_set_all_xattrs (int dfd, + const char *name, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + if (dfd == AT_FDCWD || dfd == -1) + { + return set_all_xattrs_for_path (name, xattrs, cancellable, error); + } + else + { + char buf[PATH_MAX]; + /* A workaround for the lack of lsetxattrat(), thanks to Florian Weimer: + * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html + */ + snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); + return set_all_xattrs_for_path (buf, xattrs, cancellable, error); + } +} + +/** + * glnx_fd_set_all_xattrs: + * @fd: File descriptor + * @xattrs: Extended attributes + * @cancellable: Cancellable + * @error: Error + * + * For each attribute in @xattrs, set its value on the file or + * directory referred to by @fd. This function does not remove any + * attributes not in @xattrs. + */ +gboolean +glnx_fd_set_all_xattrs (int fd, + GVariant *xattrs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int i, n; + + n = g_variant_n_children (xattrs); + for (i = 0; i < n; i++) + { + const guint8* name; + const guint8* value_data; + g_autoptr(GVariant) value = NULL; + gsize value_len; + int res; + + g_variant_get_child (xattrs, i, "(^&ay@ay)", + &name, &value); + value_data = g_variant_get_fixed_array (value, &value_len, 1); + + do + res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (G_UNLIKELY (res == -1)) + { + glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr"); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} diff --git a/glnx-xattrs.h b/glnx-xattrs.h index 360092d4..d0ea82fd 100644 --- a/glnx-xattrs.h +++ b/glnx-xattrs.h @@ -42,4 +42,17 @@ glnx_fd_get_all_xattrs (int fd, GCancellable *cancellable, GError **error); +gboolean +glnx_dfd_name_set_all_xattrs (int dfd, + const char *name, + GVariant *xattrs, + GCancellable *cancellable, + GError **error); + +gboolean +glnx_fd_set_all_xattrs (int fd, + GVariant *xattrs, + GCancellable *cancellable, + GError **error); + G_END_DECLS From 0885d6735a331bd6fedfe617d7007fed482613e2 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 17:49:58 -0500 Subject: [PATCH 006/280] dirfd: Add some gtk-doc --- glnx-dirfd.c | 59 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 8bc288b0..0435ca07 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -80,12 +80,22 @@ struct GLnxRealDirfdIterator }; typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator; +/** + * glnx_dirfd_iterator_init_at: + * @dfd: File descriptor, may be AT_FDCWD or -1 + * @path: Path, may be relative to @df + * @follow: If %TRUE and the last component of @path is a symlink, follow it + * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized + * @error: Error + * + * Initialize @out_dfd_iter from @dfd and @path. + */ gboolean -glnx_dirfd_iterator_init_at (int dfd, - const char *path, - gboolean follow, - GLnxDirFdIterator *dfd_iter, - GError **error) +glnx_dirfd_iterator_init_at (int dfd, + const char *path, + gboolean follow, + GLnxDirFdIterator *out_dfd_iter, + GError **error) { gboolean ret = FALSE; glnx_fd_close int fd = -1; @@ -102,10 +112,19 @@ glnx_dirfd_iterator_init_at (int dfd, return ret; } +/** + * glnx_dirfd_iterator_init_take_fd: + * @dfd: File descriptor - ownership is taken + * @dfd_iter: A directory iterator + * @error: Error + * + * Steal ownership of @dfd, using it to initialize @dfd_iter for + * iteration. + */ gboolean -glnx_dirfd_iterator_init_take_fd (int dfd, +glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, - GError **error) + GError **error) { gboolean ret = FALSE; GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; @@ -126,11 +145,22 @@ glnx_dirfd_iterator_init_take_fd (int dfd, return ret; } +/** + * glnx_dirfd_iterator_next_dent: + * @dfd_iter: A directory iterator + * @out_dent: (out) (transfer none): Pointer to dirent; do not free + * @cancellable: Cancellable + * @error: Error + * + * Read the next value from @dfd_iter, causing @out_dent to be + * updated. If end of stream is reached, @out_dent will be set + * to %NULL, and %TRUE will be returned. + */ gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, - struct dirent **out_dent, - GCancellable *cancellable, - GError **error) + struct dirent **out_dent, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; @@ -156,10 +186,17 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, return ret; } +/** + * glnx_dirfd_iterator_clear: + * @dfd_iter: Iterator, will be de-initialized + * + * Unset @dfd_iter, freeing any resources. If @dfd_iter is not + * initialized, do nothing. + */ void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) { - GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirIterator*) dfd_iter; /* fd is owned by dfd_iter */ (void) closedir (real_dfd_iter->d); } From 70b070b5eab16907fd6f53de100c31733a6fa7da Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 17 Feb 2015 16:02:53 -0500 Subject: [PATCH 007/280] dirfd: Fix two typos --- glnx-dirfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 0435ca07..ee25898a 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -103,7 +103,7 @@ glnx_dirfd_iterator_init_at (int dfd, if (!glnx_opendirat (dfd, path, follow, &fd, error)) goto out; - if (!glnx_dirfd_iterator_init_take_fd (fd, dfd_iter, error)) + if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error)) goto out; fd = -1; /* Transfer ownership */ @@ -196,7 +196,7 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) { - GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirIterator*) dfd_iter; + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; /* fd is owned by dfd_iter */ (void) closedir (real_dfd_iter->d); } From 64936b67001410974f1bd92803a46cdb77f89db5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 15 Feb 2015 18:18:54 -0500 Subject: [PATCH 008/280] Import console from rpm-ostree This contains some basic progress bar drawing code for now. --- Makefile-libglnx.am | 2 + glnx-console.c | 268 ++++++++++++++++++++++++++++++++++++++++++++ glnx-console.h | 47 ++++++++ libglnx.h | 1 + 4 files changed, 318 insertions(+) create mode 100644 glnx-console.c create mode 100644 glnx-console.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index a04f19e7..7463395a 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -24,6 +24,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-local-alloc.c \ $(libglnx_srcpath)/glnx-errors.h \ $(libglnx_srcpath)/glnx-errors.c \ + $(libglnx_srcpath)/glnx-console.h \ + $(libglnx_srcpath)/glnx-console.c \ $(libglnx_srcpath)/glnx-dirfd.h \ $(libglnx_srcpath)/glnx-dirfd.c \ $(libglnx_srcpath)/glnx-xattrs.h \ diff --git a/glnx-console.c b/glnx-console.c new file mode 100644 index 00000000..5ae29b10 --- /dev/null +++ b/glnx-console.c @@ -0,0 +1,268 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013,2014,2015 Colin Walters + * + * This program 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 of the licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-console.h" + +#include +#include +#include +#include +#include +#include + +static char *current_text = NULL; +static gint current_percent = -1; +static gboolean locked; + +static gboolean +stdout_is_tty (void) +{ + static gsize initialized = 0; + static gboolean stdout_is_tty_v; + + if (g_once_init_enter (&initialized)) + { + stdout_is_tty_v = isatty (1); + g_once_init_leave (&initialized, 1); + } + + return stdout_is_tty_v; +} + +static volatile guint cached_columns = 0; +static volatile guint cached_lines = 0; + +static int +fd_columns (int fd) +{ + struct winsize ws = {}; + + if (ioctl (fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_col <= 0) + return -EIO; + + return ws.ws_col; +} + +static guint +columns (void) +{ + if (G_UNLIKELY (cached_columns == 0)) + { + int c; + + c = fd_columns (STDOUT_FILENO); + + if (c <= 0) + c = 80; + + if (c > 256) + c = 256; + + cached_columns = c; + } + + return cached_columns; +} + +#if 0 +static int +fd_lines (int fd) +{ + struct winsize ws = {}; + + if (ioctl (fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +static guint +lines (void) +{ + if (G_UNLIKELY (cached_lines == 0)) + { + int l; + + l = fd_lines (STDOUT_FILENO); + + if (l <= 0) + l = 24; + + cached_lines = l; + } + + return cached_lines; +} +#endif + +static void +on_sigwinch (int signum) +{ + cached_columns = 0; + cached_lines = 0; +} + +void +glnx_console_lock (GLnxConsoleRef *console) +{ + static gsize sigwinch_initialized = 0; + + if (!stdout_is_tty ()) + return; + + g_return_if_fail (!locked); + g_return_if_fail (!console->locked); + + locked = console->locked = TRUE; + + current_percent = 0; + + if (g_once_init_enter (&sigwinch_initialized)) + { + signal (SIGWINCH, on_sigwinch); + g_once_init_leave (&sigwinch_initialized, 1); + } + + { static const char initbuf[] = { '\n', 0x1B, 0x37 }; + (void) fwrite (initbuf, 1, sizeof (initbuf), stdout); + } +} + +static void +printpad (const char *padbuf, + guint padbuf_len, + guint n) +{ + const guint d = n / padbuf_len; + const guint r = n % padbuf_len; + guint i; + + for (i = 0; i < d; i++) + fwrite (padbuf, 1, padbuf_len, stdout); + fwrite (padbuf, 1, r, stdout); +} + +/** + * glnx_console_progress_text_percent: + * @text: Show this text before the progress bar + * @percentage: An integer in the range of 0 to 100 + * + * Print to the console @text followed by an ASCII art progress bar + * whose percentage is @percentage. + * + * You must have called glnx_console_lock() before invoking this + * function. + * + * Currently, if stdout is not a tty, this function does nothing. + */ +void +glnx_console_progress_text_percent (const char *text, + guint percentage) +{ + static const char equals[] = "===================="; + const guint n_equals = sizeof (equals) - 1; + static const char spaces[] = " "; + const guint n_spaces = sizeof (spaces) - 1; + const guint ncolumns = columns (); + const guint bar_min = 10; + const guint input_textlen = text ? strlen (text) : 0; + guint textlen; + guint barlen; + + if (!stdout_is_tty ()) + return; + + g_return_if_fail (percentage >= 0 && percentage <= 100); + + if (text && !*text) + text = NULL; + + if (percentage == current_percent + && g_strcmp0 (text, current_text) == 0) + return; + + if (ncolumns < bar_min) + return; /* TODO: spinner */ + + /* Restore cursor */ + { const char beginbuf[2] = { 0x1B, 0x38 }; + (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout); + } + + textlen = MIN (input_textlen, ncolumns - bar_min); + barlen = ncolumns - textlen; + + if (textlen > 0) + { + fwrite (text, 1, textlen - 1, stdout); + fputc (' ', stdout); + } + + { + const guint nbraces = 2; + const guint textpercent_len = 5; + const guint bar_internal_len = barlen - nbraces - textpercent_len; + const guint eqlen = bar_internal_len * (percentage / 100.0); + const guint spacelen = bar_internal_len - eqlen; + + fputc ('[', stdout); + printpad (equals, n_equals, eqlen); + printpad (spaces, n_spaces, spacelen); + fputc (']', stdout); + fprintf (stdout, " %3d%%", percentage); + } + + { const guint spacelen = ncolumns - textlen - barlen; + printpad (spaces, n_spaces, spacelen); + } + + fflush (stdout); +} + +/** + * glnx_console_unlock: + * + * Print a newline, and reset all cached console progress state. + * + * This function does nothing if stdout is not a tty. + */ +void +glnx_console_unlock (GLnxConsoleRef *console) +{ + if (!stdout_is_tty ()) + return; + + g_return_if_fail (locked); + g_return_if_fail (console->locked); + + current_percent = -1; + g_clear_pointer (¤t_text, g_free); + fputc ('\n', stdout); + + locked = FALSE; +} diff --git a/glnx-console.h b/glnx-console.h new file mode 100644 index 00000000..3dfdf1bd --- /dev/null +++ b/glnx-console.h @@ -0,0 +1,47 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2013,2014,2015 Colin Walters + * + * This program 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 of the licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +struct GLnxConsoleRef { + gboolean locked; +}; + +typedef struct GLnxConsoleRef GLnxConsoleRef; + +void glnx_console_lock (GLnxConsoleRef *ref); + +void glnx_console_progress_text_percent (const char *text, + guint percentage); + +void glnx_console_unlock (GLnxConsoleRef *ref); + +static inline void +glnx_console_ref_cleanup (GLnxConsoleRef *p) +{ + glnx_console_unlock (p); +} +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup) + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index e74eedab..8ff787c2 100644 --- a/libglnx.h +++ b/libglnx.h @@ -31,5 +31,6 @@ G_BEGIN_DECLS #include #include #include +#include G_END_DECLS From fa43744c05622b3ae05ae504473c0c7486d12f37 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 18 Feb 2015 16:40:27 -0500 Subject: [PATCH 009/280] Fix includes when relying on backported g_autoptr() --- glnx-console.h | 2 +- glnx-dirfd.h | 2 +- glnx-errors.c | 2 +- glnx-errors.h | 2 +- glnx-xattrs.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/glnx-console.h b/glnx-console.h index 3dfdf1bd..081e7949 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -20,7 +20,7 @@ #pragma once -#include +#include G_BEGIN_DECLS diff --git a/glnx-dirfd.h b/glnx-dirfd.h index ccb6aa2f..27cd03b8 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include #include #include diff --git a/glnx-errors.c b/glnx-errors.c index 3cbda7e4..ab7bc0aa 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -20,7 +20,7 @@ #include "config.h" -#include +#include #include void diff --git a/glnx-errors.h b/glnx-errors.h index e51cd7e8..fffef6c9 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include G_BEGIN_DECLS diff --git a/glnx-xattrs.h b/glnx-xattrs.h index d0ea82fd..2c6abfd7 100644 --- a/glnx-xattrs.h +++ b/glnx-xattrs.h @@ -20,7 +20,7 @@ #pragma once -#include +#include #include #include #include From ffac03f49ce10c372f55b947a664fbd430ce3429 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 18 Feb 2015 08:29:03 -0500 Subject: [PATCH 010/280] .gitignore: New file This comes into play with subdir-objects. --- .gitignore | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..55b946e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# A path ostree writes to work around automake bug with +# subdir-objects +Makefile-libglnx.am.inc + +# Some standard bits +.deps +.libs +.dirstamp +*.typelib +*.la +*.lo +*.o +*.pyc +*.stamp +*~ + From d469ad2a9c6fc7dbc0675df2e648cee444963306 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 19 Feb 2015 15:33:57 -0500 Subject: [PATCH 011/280] Include autocleanups - it has the definitions which we want --- libglnx.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libglnx.h b/libglnx.h index 8ff787c2..64c1ff1c 100644 --- a/libglnx.h +++ b/libglnx.h @@ -25,7 +25,6 @@ G_BEGIN_DECLS #include -#include #include #include #include From 08d68d16ccd464854bc329e5d75fc6d2a38f7878 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 19 Feb 2015 17:53:46 -0500 Subject: [PATCH 012/280] Add cleanups for array types --- glnx-backport-autocleanups.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 8fd9b9c1..dfc8dafb 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -35,6 +35,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) From 1ebfefa565692ca033ba2d156c771d9e2c4017fc Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 20 Feb 2015 11:28:47 -0500 Subject: [PATCH 013/280] fdio: New APIs to read/write on fds, fd-relative We don't have this really in GLib, unfortunately. We do want GCancellable, but we also want to operate on raw fds where possible. The "read a file and validate as UTF-8" is a common use case of mine, and this combines that with openat(). --- Makefile-libglnx.am | 2 + glnx-fdio.c | 225 ++++++++++++++++++++++++++++++++++++++++++++ glnx-fdio.h | 50 ++++++++++ libglnx.h | 1 + 4 files changed, 278 insertions(+) create mode 100644 glnx-fdio.c create mode 100644 glnx-fdio.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 7463395a..5b4411f8 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -28,6 +28,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-console.c \ $(libglnx_srcpath)/glnx-dirfd.h \ $(libglnx_srcpath)/glnx-dirfd.c \ + $(libglnx_srcpath)/glnx-fdio.h \ + $(libglnx_srcpath)/glnx-fdio.c \ $(libglnx_srcpath)/glnx-xattrs.h \ $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ diff --git a/glnx-fdio.c b/glnx-fdio.c new file mode 100644 index 00000000..67653c26 --- /dev/null +++ b/glnx-fdio.c @@ -0,0 +1,225 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +static guint8* +glnx_fd_readall_malloc (int fd, + gsize *out_len, + gboolean nul_terminate, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + const guint maxreadlen = 4096; + int res; + struct stat stbuf; + guint8* buf = NULL; + gsize buf_allocated; + gsize buf_size = 0; + gssize bytes_read; + + do + res = fstat (fd, &stbuf); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (res == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0) + buf_allocated = stbuf.st_size; + else + buf_allocated = 16; + + buf = g_malloc (buf_allocated); + + while (TRUE) + { + gsize readlen = MIN (buf_allocated - buf_size, maxreadlen); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + do + bytes_read = read (fd, buf + buf_size, readlen); + while (G_UNLIKELY (bytes_read == -1 && errno == EINTR)); + if (G_UNLIKELY (bytes_read == -1)) + { + glnx_set_error_from_errno (error); + goto out; + } + if (bytes_read == 0) + break; + + buf_size += bytes_read; + if (buf_allocated - buf_size < maxreadlen) + buf = g_realloc (buf, buf_allocated *= 2); + } + + if (nul_terminate) + { + if (buf_allocated - buf_size == 0) + buf = g_realloc (buf, buf_allocated + 1); + buf[buf_size] = '\0'; + } + + success = TRUE; + out: + if (success) + { + *out_len = buf_size; + return buf; + } + g_free (buf); + return NULL; +} + +/** + * glnx_fd_readall_bytes: + * @fd: A file descriptor + * @cancellable: Cancellable: + * @error: Error + * + * Read all data from file descriptor @fd into a #GBytes. It's + * recommended to only use this for small files. + * + * Returns: (transfer full): A newly allocated #GBytes + */ +GBytes * +glnx_fd_readall_bytes (int fd, + GCancellable *cancellable, + GError **error) +{ + guint8 *buf; + gsize len; + + buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error); + if (!buf) + return NULL; + + return g_bytes_new_take (buf, len); +} + +/** + * glnx_fd_readall_utf8: + * @fd: A file descriptor + * @out_len: (out): Returned length + * @cancellable: Cancellable: + * @error: Error + * + * Read all data from file descriptor @fd, validating + * the result as UTF-8. + * + * Returns: (transfer full): A string validated as UTF-8, or %NULL on error. + */ +char * +glnx_fd_readall_utf8 (int fd, + gsize *out_len, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + guint8 *buf; + gsize len; + + buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error); + if (!buf) + goto out; + + if (!g_utf8_validate ((char*)buf, len, NULL)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "Invalid UTF-8"); + goto out; + } + + success = TRUE; + out: + if (success) + { + if (out_len) + *out_len = len; + return (char*)buf; + } + g_free (buf); + return NULL; +} + +/** + * glnx_file_get_contents_utf8_at: + * @dfd: Directory file descriptor + * @subpath: Path relative to @dfd + * @out_len: (out) (allow-none): Optional length + * @cancellable: Cancellable + * @error: Error + * + * Read the entire contents of the file referred + * to by @dfd and @subpath, validate the result as UTF-8. + * The length is optionally stored in @out_len. + * + * Returns: (transfer full): UTF-8 validated text, or %NULL on error + */ +char * +glnx_file_get_contents_utf8_at (int dfd, + const char *subpath, + gsize *out_len, + GCancellable *cancellable, + GError **error) +{ + gboolean success = FALSE; + glnx_fd_close int fd = -1; + char *buf; + gsize len; + + do + fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC); + while (G_UNLIKELY (fd == -1 && errno == EINTR)); + if (G_UNLIKELY (fd == -1)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error); + if (G_UNLIKELY(!buf)) + goto out; + + success = TRUE; + out: + if (success) + { + if (out_len) + *out_len = len; + return buf; + } + g_free (buf); + return NULL; +} diff --git a/glnx-fdio.h b/glnx-fdio.h new file mode 100644 index 00000000..91e6aa60 --- /dev/null +++ b/glnx-fdio.h @@ -0,0 +1,50 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +GBytes * +glnx_fd_readall_bytes (int fd, + GCancellable *cancellable, + GError **error); + +char * +glnx_fd_readall_utf8 (int fd, + gsize *out_len, + GCancellable *cancellable, + GError **error); + +char * +glnx_file_get_contents_utf8_at (int dfd, + const char *subpath, + gsize *out_len, + GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index 64c1ff1c..3e815851 100644 --- a/libglnx.h +++ b/libglnx.h @@ -31,5 +31,6 @@ G_BEGIN_DECLS #include #include #include +#include G_END_DECLS From 1288bd850829c66935d3f12c3363a32c87c1421c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 20 Feb 2015 13:39:39 -0500 Subject: [PATCH 014/280] xattrs: Migrate some code from ostree here This also uses GBytes and avoids malloc where possible. --- glnx-xattrs.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-xattrs.h | 20 ++++++++ 2 files changed, 149 insertions(+) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 3421cccf..b5a59228 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -395,3 +395,132 @@ glnx_fd_set_all_xattrs (int fd, out: return ret; } + +/** + * glnx_lgetxattrat: + * @dfd: Directory file descriptor + * @subpath: Subpath + * @attribute: Extended attribute to retrieve + * @error: Error + * + * Retrieve an extended attribute value, relative to a directory file + * descriptor. + */ +GBytes * +glnx_lgetxattrat (int dfd, + const char *subpath, + const char *attribute, + GError **error) +{ + char pathbuf[PATH_MAX]; + GBytes *bytes = NULL; + ssize_t bytes_read, real_size; + guint8 *buf; + + snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); + + do + bytes_read = lgetxattr (pathbuf, attribute, NULL, 0); + while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); + if (G_UNLIKELY (bytes_read < 0)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = g_malloc (bytes_read); + do + real_size = lgetxattr (pathbuf, attribute, buf, bytes_read); + while (G_UNLIKELY (real_size < 0 && errno == EINTR)); + if (G_UNLIKELY (real_size < 0)) + { + glnx_set_error_from_errno (error); + g_free (buf); + goto out; + } + + bytes = g_bytes_new_take (buf, real_size); + out: + return bytes; +} + +/** + * glnx_fgetxattr_bytes: + * @fd: Directory file descriptor + * @attribute: Extended attribute to retrieve + * @error: Error + * + * Returns: (transfer full): An extended attribute value, or %NULL on error + */ +GBytes * +glnx_fgetxattr_bytes (int fd, + const char *attribute, + GError **error) +{ + GBytes *bytes = NULL; + ssize_t bytes_read, real_size; + guint8 *buf; + + do + bytes_read = fgetxattr (fd, attribute, NULL, 0); + while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); + if (G_UNLIKELY (bytes_read < 0)) + { + glnx_set_error_from_errno (error); + goto out; + } + + buf = g_malloc (bytes_read); + do + real_size = fgetxattr (fd, attribute, buf, bytes_read); + while (G_UNLIKELY (real_size < 0 && errno == EINTR)); + if (G_UNLIKELY (real_size < 0)) + { + glnx_set_error_from_errno (error); + g_free (buf); + goto out; + } + + bytes = g_bytes_new_take (buf, real_size); + out: + return bytes; +} + +/** + * glnx_lsetxattrat: + * @dfd: Directory file descriptor + * @subpath: Path + * @attribute: An attribute name + * @value: (array length=len) (element-type guint8): Attribute value + * @len: Length of @value + * @flags: Flags, containing either XATTR_CREATE or XATTR_REPLACE + * @error: Error + * + * Set an extended attribute, relative to a directory file descriptor. + */ +gboolean +glnx_lsetxattrat (int dfd, + const char *subpath, + const char *attribute, + const guint8 *value, + gsize len, + int flags, + GError **error) +{ + char pathbuf[PATH_MAX]; + int res; + + snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); + + do + res = lsetxattr (subpath, attribute, value, len, flags); + while (G_UNLIKELY (res == -1 && errno == EINTR)); + if (G_UNLIKELY (res == -1)) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + return TRUE; +} + diff --git a/glnx-xattrs.h b/glnx-xattrs.h index 2c6abfd7..410c722f 100644 --- a/glnx-xattrs.h +++ b/glnx-xattrs.h @@ -55,4 +55,24 @@ glnx_fd_set_all_xattrs (int fd, GCancellable *cancellable, GError **error); +GBytes * +glnx_lgetxattrat (int dfd, + const char *subpath, + const char *attribute, + GError **error); + +GBytes * +glnx_fgetxattr_bytes (int fd, + const char *attribute, + GError **error); + +gboolean +glnx_lsetxattrat (int dfd, + const char *subpath, + const char *attribute, + const guint8 *value, + gsize len, + int flags, + GError **error); + G_END_DECLS From 37082ed867f2c3e545b178aef2f4c1b48ea2c484 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 25 Feb 2015 21:44:42 -0500 Subject: [PATCH 015/280] backports: g_autofree --- glnx-backport-autocleanups.h | 10 +++++++++- glnx-backport-autoptr.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index dfc8dafb..daa89a9b 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -21,7 +21,15 @@ #include -#if !GLIB_CHECK_VERSION(2, 43, 3) +#if !GLIB_CHECK_VERSION(2, 43, 4) + +static inline void +g_autoptr_cleanup_generic_gfree (void *p) +{ + void **pp = (void**)p; + if (*pp) + g_free (*pp); +} G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) diff --git a/glnx-backport-autoptr.h b/glnx-backport-autoptr.h index fd5f5d01..b36919dc 100644 --- a/glnx-backport-autoptr.h +++ b/glnx-backport-autoptr.h @@ -55,6 +55,7 @@ G_BEGIN_DECLS G_GNUC_END_IGNORE_DEPRECATIONS #define g_autoptr(TypeName) _GLIB_CLEANUP(_GLIB_AUTOPTR_FUNC_NAME(TypeName)) _GLIB_AUTOPTR_TYPENAME(TypeName) #define g_auto(TypeName) _GLIB_CLEANUP(_GLIB_AUTO_FUNC_NAME(TypeName)) TypeName +#define g_autofree _GLIB_CLEANUP(g_autoptr_cleanup_generic_gfree) /** * g_steal_pointer: From 39c6393876d9dfef607e960a951b3bbae2ef5f6a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Mon, 23 Feb 2015 12:54:47 +0100 Subject: [PATCH 016/280] Makefile-libglnx.am: use README.md not README Signed-off-by: Giuseppe Scrivano --- Makefile-libglnx.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 5b4411f8..f2c65491 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -15,7 +15,7 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -EXTRA_DIST += $(libglnx_srcpath)/README $(libglnx_srcpath)/COPYING +EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ From 9a7277889a8901a60206e58bafe1a6fcd2a50bc9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 3 Mar 2015 08:43:15 -0500 Subject: [PATCH 017/280] dfditer: Unset initialized variable when cleared This allows reusing an iterator struct. --- glnx-dirfd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index ee25898a..af4cab82 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -199,4 +199,5 @@ glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; /* fd is owned by dfd_iter */ (void) closedir (real_dfd_iter->d); + real_dfd_iter->initialized = FALSE; } From 162d1f6b58c9b501f47080e99aa1fd36864a89f9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 25 Feb 2015 21:28:24 -0500 Subject: [PATCH 018/280] fdio: Add glnx_file_copy_at() This will allow deleting some code from OSTree for the config file merging. We're reusing some code from systemd, which a nice modern clean codebase, and among other things this gets us BTRFS reflinking (if available) again. --- glnx-fdio.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-fdio.h | 22 ++++ 2 files changed, 375 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 67653c26..e0a240d5 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -2,6 +2,9 @@ * * Copyright (C) 2014,2015 Colin Walters . * + * Portions derived from systemd: + * Copyright 2010 Lennart Poettering + * * 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 @@ -22,9 +25,19 @@ #include #include +#include +#include +#include +#include +#include +/* See linux.git/fs/btrfs/ioctl.h */ +#define BTRFS_IOCTL_MAGIC 0x94 +#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) #include #include +#include +#include #include static guint8* @@ -223,3 +236,343 @@ glnx_file_get_contents_utf8_at (int dfd, g_free (buf); return NULL; } + +/** + * glnx_readlinkat_malloc: + * @dfd: Directory file descriptor + * @subpath: Subpath + * @cancellable: Cancellable + * @error: Error + * + * Read the value of a symlink into a dynamically + * allocated buffer. + */ +char * +glnx_readlinkat_malloc (int dfd, + const char *subpath, + GCancellable *cancellable, + GError **error) +{ + size_t l = 100; + + for (;;) + { + char *c; + ssize_t n; + + c = g_malloc (l); + n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); + if (n < 0) + { + glnx_set_error_from_errno (error); + g_free (c); + return FALSE; + } + + if ((size_t) n < l-1) + { + c[n] = 0; + return c; + } + + g_free (c); + l *= 2; + } + + g_assert_not_reached (); +} + +static gboolean +copy_symlink_at (int src_dfd, + const char *src_subpath, + const struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autofree char *buf = NULL; + + buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error); + if (!buf) + goto out; + + if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) + { + g_autoptr(GVariant) xattrs = NULL; + + if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs, + cancellable, error)) + goto out; + + if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs, + cancellable, error)) + goto out; + } + + if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath, + src_stbuf->st_uid, src_stbuf->st_gid, + AT_SYMLINK_NOFOLLOW)) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +#define COPY_BUFFER_SIZE (16*1024) + +/* From systemd */ + +static int btrfs_reflink(int infd, int outfd) { + int r; + + g_return_val_if_fail(infd >= 0, -1); + g_return_val_if_fail(outfd >= 0, -1); + + r = ioctl(outfd, BTRFS_IOC_CLONE, infd); + if (r < 0) + return -errno; + + return 0; +} + +static int loop_write(int fd, const void *buf, size_t nbytes) { + const uint8_t *p = buf; + + g_return_val_if_fail(fd >= 0, -1); + g_return_val_if_fail(buf, -1); + + errno = 0; + + while (nbytes > 0) { + ssize_t k; + + k = write(fd, p, nbytes); + if (k < 0) { + if (errno == EINTR) + continue; + + return -errno; + } + + if (k == 0) /* Can't really happen */ + return -EIO; + + p += k; + nbytes -= k; + } + + return 0; +} + +static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { + bool try_sendfile = true; + int r; + + g_return_val_if_fail (fdf >= 0, -1); + g_return_val_if_fail (fdt >= 0, -1); + + /* Try btrfs reflinks first. */ + if (try_reflink && max_bytes == (off_t) -1) { + r = btrfs_reflink(fdf, fdt); + if (r >= 0) + return r; + } + + for (;;) { + size_t m = COPY_BUFFER_SIZE; + ssize_t n; + + if (max_bytes != (off_t) -1) { + + if (max_bytes <= 0) + return -EFBIG; + + if ((off_t) m > max_bytes) + m = (size_t) max_bytes; + } + + /* First try sendfile(), unless we already tried */ + if (try_sendfile) { + + n = sendfile(fdt, fdf, NULL, m); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_sendfile = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; + } + + /* As a fallback just copy bits by hand */ + { + char buf[m]; + + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) /* EOF */ + break; + + r = loop_write(fdt, buf, (size_t) n); + if (r < 0) + return r; + } + + next: + if (max_bytes != (off_t) -1) { + g_assert(max_bytes >= n); + max_bytes -= n; + } + } + + return 0; +} + +/** + * glnx_file_copy_at: + * @src_dfd: Source directory fd + * @src_subpath: Subpath relative to @src_dfd + * @dest_dfd: Target directory fd + * @dest_subpath: Destination name + * @copyflags: Flags + * @cancellable: cancellable + * @error: Error + * + * Perform a full copy of the regular file or + * symbolic link from @src_subpath to @dest_subpath. + * + * If @src_subpath is anything other than a regular + * file or symbolic link, an error will be returned. + */ +gboolean +glnx_file_copy_at (int src_dfd, + const char *src_subpath, + struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + int r; + int dest_open_flags; + struct timespec ts[2]; + glnx_fd_close int src_fd = -1; + glnx_fd_close int dest_fd = -1; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + if (S_ISLNK (src_stbuf->st_mode)) + { + return copy_symlink_at (src_dfd, src_subpath, src_stbuf, + dest_dfd, dest_subpath, + copyflags, + cancellable, error); + } + else if (!S_ISREG (src_stbuf->st_mode)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Cannot copy non-regular/non-symlink file: %s", src_subpath); + goto out; + } + + src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW)); + if (src_fd == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY; + if (!(copyflags & GLNX_FILE_COPY_OVERWRITE)) + dest_open_flags |= O_EXCL; + else + dest_open_flags |= O_TRUNC; + + dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags)); + if (dest_fd == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE); + if (r < 0) + { + errno = -r; + glnx_set_error_from_errno (error); + goto out; + } + + if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ts[0] = src_stbuf->st_atim; + ts[1] = src_stbuf->st_mtim; + (void) futimens (dest_fd, ts); + + if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) + { + g_autoptr(GVariant) xattrs = NULL; + + if (!glnx_fd_get_all_xattrs (src_fd, &xattrs, + cancellable, error)) + goto out; + + if (!glnx_fd_set_all_xattrs (dest_fd, xattrs, + cancellable, error)) + goto out; + } + + if (copyflags & GLNX_FILE_COPY_DATASYNC) + { + if (fdatasync (dest_fd) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + r = close (dest_fd); + dest_fd = -1; + if (r < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + if (!ret) + (void) unlinkat (dest_dfd, dest_subpath, 0); + return ret; +} diff --git a/glnx-fdio.h b/glnx-fdio.h index 91e6aa60..688eeb25 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -47,4 +47,26 @@ glnx_file_get_contents_utf8_at (int dfd, GCancellable *cancellable, GError **error); +char * +glnx_readlinkat_malloc (int dfd, + const char *subpath, + GCancellable *cancellable, + GError **error); + +typedef enum { + GLNX_FILE_COPY_OVERWRITE, + GLNX_FILE_COPY_NOXATTRS, + GLNX_FILE_COPY_DATASYNC +} GLnxFileCopyFlags; + +gboolean +glnx_file_copy_at (int src_dfd, + const char *src_subpath, + struct stat *src_stbuf, + int dest_dfd, + const char *dest_subpath, + GLnxFileCopyFlags copyflags, + GCancellable *cancellable, + GError **error); + G_END_DECLS From a11b2eb20aa20db8076eb5fc86848d50cb6b6477 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 4 Mar 2015 18:21:38 -0500 Subject: [PATCH 019/280] Import libcontainer code from rpm-ostree Now that this module is Linux specific, we can more cleanly depend on it here. --- Makefile-libglnx.am | 2 + glnx-libcontainer.c | 249 ++++++++++++++++++++++++++++++++++++++++++++ glnx-libcontainer.h | 44 ++++++++ libglnx.h | 1 + 4 files changed, 296 insertions(+) create mode 100644 glnx-libcontainer.c create mode 100644 glnx-libcontainer.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index f2c65491..87754188 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -30,6 +30,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-dirfd.c \ $(libglnx_srcpath)/glnx-fdio.h \ $(libglnx_srcpath)/glnx-fdio.c \ + $(libglnx_srcpath)/glnx-libcontainer.h \ + $(libglnx_srcpath)/glnx-libcontainer.c \ $(libglnx_srcpath)/glnx-xattrs.h \ $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c new file mode 100644 index 00000000..dafd8ad4 --- /dev/null +++ b/glnx-libcontainer.c @@ -0,0 +1,249 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Portions derived from src/nspawn/nspawn.c: + * Copyright 2010 Lennart Poettering + * + * Copyright (C) 2014,2015 Colin Walters + * + * This program 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 of the licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glnx-libcontainer.h" + +#include "glnx-backport-autocleanups.h" +#include "glnx-local-alloc.h" + +static gboolean container_available = TRUE; + +static void _perror_fatal (const char *message) __attribute__ ((noreturn)); + +static void +_perror_fatal (const char *message) +{ + perror (message); + exit (1); +} + +void +glnx_libcontainer_set_not_available (void) +{ + container_available = FALSE; +} + +gboolean +glnx_libcontainer_get_available (void) +{ + return container_available; +} + +gboolean +glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) +{ + gboolean ret = FALSE; + + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND): %s", + path, + g_strerror (errsv)); + goto out; + } + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND | MS_RDONLY): %s", + path, + g_strerror (errsv)); + goto out; + } + + ret = TRUE; + out: + return ret; +} + + +/* Based on code from nspawn.c */ +int +glnx_libcontainer_make_api_mounts (const char *dest) +{ + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + gboolean fatal; + } MountPoint; + + static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, TRUE }, /* Bind mount first */ + { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, TRUE }, /* Then, make it r/o */ + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, TRUE }, + { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, FALSE }, /* Bind mount first */ + { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, FALSE }, /* Then, make it r/o */ + }; + + unsigned k; + + for (k = 0; k < G_N_ELEMENTS(mount_table); k++) + { + g_autofree char *where = NULL; + int t; + + where = g_build_filename (dest, mount_table[k].where, NULL); + + t = mkdir (where, 0755); + if (t < 0 && errno != EEXIST) + { + if (!mount_table[k].fatal) + continue; + return -1; + } + + if (mount (mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + mount_table[k].options) < 0) + { + if (errno == ENOENT && !mount_table[k].fatal) + continue; + return -1; + } + } + + return 0; +} + +int +glnx_libcontainer_prep_dev (const char *dest_devdir) +{ + glnx_fd_close int src_fd = -1; + glnx_fd_close int dest_fd = -1; + struct stat stbuf; + guint i; + static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; + + src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (src_fd == -1) + return -1; + + dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (dest_fd == -1) + return -1; + + for (i = 0; i < G_N_ELEMENTS (devnodes); i++) + { + const char *nodename = devnodes[i]; + + if (fstatat (src_fd, nodename, &stbuf, 0) == -1) + { + if (errno == ENOENT) + continue; + else + return -1; + } + + if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0) + return -1; + if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0) + return -1; + } + + return 0; +} + +pid_t +glnx_libcontainer_run_in_root (const char *dest, + const char *binary, + char **argv) +{ + const int cloneflags = + SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS; + pid_t child; + + if (container_available) + { + if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0) + return -1; + } + else + { + if ((child = fork ()) < 0) + return -1; + } + + if (child != 0) + return child; + + if (container_available) + { + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) + _perror_fatal ("mount: "); + + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) + _perror_fatal ("mount (MS_NOSUID): "); + } + + if (chdir (dest) != 0) + _perror_fatal ("chdir: "); + + if (container_available) + { + if (glnx_libcontainer_make_api_mounts (dest) != 0) + _perror_fatal ("preparing api mounts: "); + + if (glnx_libcontainer_prep_dev ("dev") != 0) + _perror_fatal ("preparing /dev: "); + + if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + _perror_fatal ("mount (MS_BIND)"); + + if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0) + _perror_fatal ("mount (MS_MOVE)"); + } + + if (chroot (".") != 0) + _perror_fatal ("chroot: "); + + if (chdir ("/") != 0) + _perror_fatal ("chdir: "); + + if (execv (binary, argv) != 0) + _perror_fatal ("execl: "); + + g_assert_not_reached (); +} diff --git a/glnx-libcontainer.h b/glnx-libcontainer.h new file mode 100644 index 00000000..16cdb654 --- /dev/null +++ b/glnx-libcontainer.h @@ -0,0 +1,44 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters + * + * This program 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 of the licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void glnx_libcontainer_set_not_available (void); +gboolean glnx_libcontainer_get_available (void); + +gboolean glnx_libcontainer_bind_mount_readonly (const char *path, GError **error); + +int glnx_libcontainer_make_api_mounts (const char *dest); +int glnx_libcontainer_prep_dev (const char *dest); + +pid_t glnx_libcontainer_run_in_root (const char *dest, + const char *binary, + char **argv); diff --git a/libglnx.h b/libglnx.h index 3e815851..89defac1 100644 --- a/libglnx.h +++ b/libglnx.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS #include #include #include +#include #include #include From 175502e5bee236123d27adf4be01e8ba8fe2b178 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 5 Mar 2015 09:02:48 -0500 Subject: [PATCH 020/280] Add glnx_basename() We have to wrap the glibc version to ensure we get the right version, otherwise depending on the variance of includes we may end up crashing if we get the POSIX version. --- glnx-fdio.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index 688eeb25..19f938c7 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -26,9 +26,24 @@ #include #include #include +/* From systemd/src/shared/util.h */ +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the XDG + * version which is really broken. */ +#include +#undef basename G_BEGIN_DECLS +/* Irritatingly, g_basename() which is what we want + * is deprecated. + */ +static inline +const char *glnx_basename (const char *path) +{ + return (basename) (path); +} + GBytes * glnx_fd_readall_bytes (int fd, GCancellable *cancellable, From c92adab47af5d3e75f4e4e73c50e02637f226ba8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 9 Mar 2015 21:20:10 -0400 Subject: [PATCH 021/280] fdio: Add missing mode argument Spotted by fortify-source. --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index e0a240d5..77be6e11 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -509,7 +509,7 @@ glnx_file_copy_at (int src_dfd, else dest_open_flags |= O_TRUNC; - dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags)); + dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode)); if (dest_fd == -1) { glnx_set_error_from_errno (error); From cf2a89f50633df330858efe963263b0577010718 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 17 Mar 2015 13:40:35 -0400 Subject: [PATCH 022/280] Add glnx_dirfd_canonicalize() We want to honor `-1 == AT_FDCWD`. --- glnx-dirfd.c | 3 +++ glnx-dirfd.h | 17 +++++++++++++++++ glnx-fdio.c | 8 ++++++++ glnx-shutil.c | 4 +++- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index af4cab82..4b7d5a04 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -43,6 +43,9 @@ glnx_opendirat_with_errno (int dfd, int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY; if (!follow) flags |= O_NOFOLLOW; + + dfd = glnx_dirfd_canonicalize (dfd); + return openat (dfd, path, flags); } diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 27cd03b8..585d16af 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -27,6 +27,23 @@ #include G_BEGIN_DECLS + +/** + * glnx_dirfd_canonicalize: + * @fd: A directory file descriptor + * + * It's often convenient in programs to use `-1` for "unassigned fd", + * and also because gobject-introspection doesn't support `AT_FDCWD`, + * libglnx honors `-1` to mean `AT_FDCWD`. This small inline function + * canonicalizes `-1 -> AT_FDCWD`. + */ +static inline int +glnx_dirfd_canonicalize (int fd) +{ + if (fd == -1) + return AT_FDCWD; + return fd; +} struct GLnxDirFdIterator { gboolean initialized; diff --git a/glnx-fdio.c b/glnx-fdio.c index 77be6e11..63dea2b9 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -35,6 +35,7 @@ #define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) #include +#include #include #include #include @@ -212,6 +213,8 @@ glnx_file_get_contents_utf8_at (int dfd, char *buf; gsize len; + dfd = glnx_dirfd_canonicalize (dfd); + do fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC); while (G_UNLIKELY (fd == -1 && errno == EINTR)); @@ -255,6 +258,8 @@ glnx_readlinkat_malloc (int dfd, { size_t l = 100; + dfd = glnx_dirfd_canonicalize (dfd); + for (;;) { char *c; @@ -482,6 +487,9 @@ glnx_file_copy_at (int src_dfd, if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; + src_dfd = glnx_dirfd_canonicalize (src_dfd); + dest_dfd = glnx_dirfd_canonicalize (dest_dfd); + if (S_ISLNK (src_stbuf->st_mode)) { return copy_symlink_at (src_dfd, src_subpath, src_stbuf, diff --git a/glnx-shutil.c b/glnx-shutil.c index 9fcb65a0..967e3649 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -118,7 +118,7 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, /** * glnx_shutil_rm_rf_at: - * @dfd: A directory file descriptor, or -1 for current + * @dfd: A directory file descriptor, or `AT_FDCWD` or `-1` for current * @path: Path * @cancellable: Cancellable * @error: Error @@ -137,6 +137,8 @@ glnx_shutil_rm_rf_at (int dfd, glnx_fd_close int target_dfd = -1; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + dfd = glnx_dirfd_canonicalize (dfd); + /* With O_NOFOLLOW first */ target_dfd = openat (dfd, path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); From 08d1339f9a61c0b437a623e68ebf2c64258d6087 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 19 Mar 2015 20:49:11 -0400 Subject: [PATCH 023/280] console: Make glnx_console_lines and columns public They'll be used by rpm-ostree at least. --- glnx-console.c | 22 +++++++++++++++------- glnx-console.h | 4 ++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index 5ae29b10..bf77b565 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -65,8 +65,13 @@ fd_columns (int fd) return ws.ws_col; } -static guint -columns (void) +/** + * glnx_console_columns: + * + * Returns: The number of columns for terminal output + */ +guint +glnx_console_columns (void) { if (G_UNLIKELY (cached_columns == 0)) { @@ -86,7 +91,6 @@ columns (void) return cached_columns; } -#if 0 static int fd_lines (int fd) { @@ -101,8 +105,13 @@ fd_lines (int fd) return ws.ws_row; } -static guint -lines (void) +/** + * glnx_console_lines: + * + * Returns: The number of lines for terminal output + */ +guint +glnx_console_lines (void) { if (G_UNLIKELY (cached_lines == 0)) { @@ -118,7 +127,6 @@ lines (void) return cached_lines; } -#endif static void on_sigwinch (int signum) @@ -188,7 +196,7 @@ glnx_console_progress_text_percent (const char *text, const guint n_equals = sizeof (equals) - 1; static const char spaces[] = " "; const guint n_spaces = sizeof (spaces) - 1; - const guint ncolumns = columns (); + const guint ncolumns = glnx_console_columns (); const guint bar_min = 10; const guint input_textlen = text ? strlen (text) : 0; guint textlen; diff --git a/glnx-console.h b/glnx-console.h index 081e7949..9e259a52 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -37,6 +37,10 @@ void glnx_console_progress_text_percent (const char *text, void glnx_console_unlock (GLnxConsoleRef *ref); +guint glnx_console_lines (void); + +guint glnx_console_columns (void); + static inline void glnx_console_ref_cleanup (GLnxConsoleRef *p) { From d8a3d3b821dc441475638d2d38a62a57f83d3d28 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 20 Mar 2015 11:53:57 -0400 Subject: [PATCH 024/280] README.md: Add some porting bits --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 1767a2d6..5627c6af 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,17 @@ libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem It is for modules which depend on both GLib and Linux, intended to be used as a git submodule. + +Porting from libgsystem +----------------------- + +For all of the filesystem access code, libglnx exposes only +fd-relative API, not `GFile*`. It does use `GCancellable` where +applicable. + +For local allocation macros, you should start using the `g_auto` +macros from GLib. A backport is included in libglnx. There are a few +APIs not defined in GLib yet, such as `glnx_fd_close`. + +`gs_transfer_out_value` is replaced by `g_steal_pointer`. + From d59a63e3e650aa75a055e4ede523790d60645435 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 1 Apr 2015 05:42:26 -0400 Subject: [PATCH 025/280] Switch to using glibc xattrs See https://github.com/GNOME/ostree/pull/78 --- glnx-fdio.h | 2 +- glnx-xattrs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 19f938c7..5765a02c 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include /* From systemd/src/shared/util.h */ /* When we include libgen.h because we need dirname() we immediately * undefine basename() since libgen.h defines it as a macro to the XDG diff --git a/glnx-xattrs.h b/glnx-xattrs.h index 410c722f..a566a224 100644 --- a/glnx-xattrs.h +++ b/glnx-xattrs.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include G_BEGIN_DECLS From 19885b8a20053082543ceb238197315de1cc0bf6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 7 Apr 2015 12:29:07 -0400 Subject: [PATCH 026/280] shutil: Add mkdir -p API I looked at the systemd code but it didn't have a variant of mkdir_parents that used `*at()`. This is a fresh implementation, with the risk that entails. However I am changing libgsystem to call it now for testing, and libgsystem APIs are covered by ostree usage at least. --- glnx-shutil.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-shutil.h | 7 ++++ 2 files changed, 105 insertions(+) diff --git a/glnx-shutil.c b/glnx-shutil.c index 967e3649..e4df9aec 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -188,3 +188,101 @@ glnx_shutil_rm_rf_at (int dfd, out: return ret; } + +static gboolean +mkdir_p_at_internal (int dfd, + char *path, + int mode, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gboolean did_recurse = FALSE; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + goto out; + + again: + if (mkdirat (dfd, path, mode) == -1) + { + if (errno == ENOENT) + { + char *lastslash; + + g_assert (!did_recurse); + + lastslash = strrchr (path, '/'); + g_assert (lastslash != NULL); + /* Note we can mutate the buffer as we dup'd it */ + *lastslash = '\0'; + + if (!glnx_shutil_mkdir_p_at (dfd, path, mode, + cancellable, error)) + goto out; + + /* Now restore it for another mkdir attempt */ + *lastslash = '/'; + + did_recurse = TRUE; + goto again; + } + else if (errno == EEXIST) + { + /* Fall through; it may not have been a directory, + * but we'll find that out on the next call up. + */ + } + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + +/** + * glnx_shutil_mkdir_p_at: + * @dfd: Directory fd + * @path: Directory path to be created + * @mode: Mode for newly created directories + * @cancellable: Cancellable + * @error: Error + * + * Similar to g_mkdir_with_parents(), except operates relative to the + * directory fd @dfd. + */ +gboolean +glnx_shutil_mkdir_p_at (int dfd, + const char *path, + int mode, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + struct stat stbuf; + + /* Fast path stat to see whether it already exists */ + if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0) + { + if (S_ISDIR (stbuf.st_mode)) + { + ret = TRUE; + goto out; + } + } + + { + char *buf = strdupa (path); + + if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error)) + goto out; + } + + ret = TRUE; + out: + return ret; +} diff --git a/glnx-shutil.h b/glnx-shutil.h index 0c53fd86..8cc732c3 100644 --- a/glnx-shutil.h +++ b/glnx-shutil.h @@ -30,4 +30,11 @@ glnx_shutil_rm_rf_at (int dfd, GCancellable *cancellable, GError **error); +gboolean +glnx_shutil_mkdir_p_at (int dfd, + const char *path, + int mode, + GCancellable *cancellable, + GError **error); + G_END_DECLS From e72bbdebb03f5d39c7f32869299ca4284786344c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 7 Apr 2015 12:34:30 -0400 Subject: [PATCH 027/280] fdio: Include for basename Most callers already included `string.h` which is why I didn't see this earlier. --- glnx-fdio.h | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index 5765a02c..47368dfc 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -25,6 +25,7 @@ #include #include #include +#include #include /* From systemd/src/shared/util.h */ /* When we include libgen.h because we need dirname() we immediately From 16f02afb2b8a764ff697fbad133f319ab9dea97f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 8 Apr 2015 05:58:31 -0400 Subject: [PATCH 028/280] build: Drop leftover -lattr See previous commits to drop libattr dependency. --- Makefile-libglnx.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 87754188..9ccceeb8 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -41,4 +41,4 @@ libglnx_la_SOURCES = \ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic -libglnx_la_LIBADD = $(libglnx_libs) -lattr +libglnx_la_LIBADD = $(libglnx_libs) From 55220bdbf2363b9826ad05c7537abe057e69bacc Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 8 Apr 2015 05:59:16 -0400 Subject: [PATCH 029/280] shutil: Drop unnecessary new block scope --- glnx-shutil.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/glnx-shutil.c b/glnx-shutil.c index e4df9aec..281e5bf0 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -264,6 +264,7 @@ glnx_shutil_mkdir_p_at (int dfd, { gboolean ret = FALSE; struct stat stbuf; + char *buf; /* Fast path stat to see whether it already exists */ if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0) @@ -275,12 +276,10 @@ glnx_shutil_mkdir_p_at (int dfd, } } - { - char *buf = strdupa (path); + buf = strdupa (path); - if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error)) - goto out; - } + if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error)) + goto out; ret = TRUE; out: From dc47528f51427f8fbb274929bdbf5a8c1380e8d8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 8 Apr 2015 21:04:15 -0400 Subject: [PATCH 030/280] fdio: Add glnx_file_replace_contents_at() Sort of similar to `g_file_replace_contents()` but `*at()`. Will be used for further conversion of OSTree to `*at()`. --- glnx-fdio.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-fdio.h | 22 ++++++++++++ 2 files changed, 121 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 63dea2b9..42fc10ab 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -584,3 +585,101 @@ glnx_file_copy_at (int src_dfd, (void) unlinkat (dest_dfd, dest_subpath, 0); return ret; } + +/** + * glnx_file_replace_contents_utf8_at: + * @dfd: Directory fd + * @subpath: Subpath + * @buf: (array len=len) (element-type guint8): File contents + * @len: Length (if `-1`, assume @buf is `NUL` terminated) + * @flags: Flags + * @cancellable: Cancellable + * @error: Error + * + * Atomically replace the contents of @subpath (relative to @dfd) with + * @buf. By default, if the file already existed, fdatasync() will be + * used before rename() to ensure stable contents. This and other + * behavior can be controlled via @flags. + * + * Note that no metadata from the existing file is preserved, such as + * uid/gid or extended attributes. + */ +gboolean +glnx_file_replace_contents_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + int mode, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + /* We use the /proc/self trick as there's no mkostemp_at() yet */ + g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd); + glnx_fd_close int fd = -1; + + if ((fd = mkostemp (tmppath, O_CLOEXEC)) == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (fchmod (fd, mode) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (len == -1) + len = strlen ((char*)buf); + + if (posix_fallocate (fd, 0, len)) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (loop_write (fd, buf, len) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) + { + struct stat stbuf; + gboolean do_sync; + + if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; + } + else + do_sync = TRUE; + + if (do_sync) + { + if (fdatasync (fd) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + + if (renameat (dfd, tmppath, dfd, subpath) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + ret = TRUE; + out: + return ret; +} diff --git a/glnx-fdio.h b/glnx-fdio.h index 47368dfc..a380c1f8 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -63,6 +63,28 @@ glnx_file_get_contents_utf8_at (int dfd, GCancellable *cancellable, GError **error); +/** + * GLnxFileReplaceFlags: + * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist + * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync() + * + * Flags controlling file replacement. + */ +typedef enum { + GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0), + GLNX_FILE_REPLACE_NODATASYNC = (1 << 1), +} GLnxFileReplaceFlags; + +gboolean +glnx_file_replace_contents_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + int mode, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error); + char * glnx_readlinkat_malloc (int dfd, const char *subpath, From 376219a9c276237f21c2c5aa1f0b7875a89586b6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 8 Apr 2015 21:31:43 -0400 Subject: [PATCH 031/280] fdio: Also add a replace variant that takes mode/uid/gid This will be used for OSTree too. --- glnx-fdio.c | 58 +++++++++++++++++++++++++++++++++++++++++++++-------- glnx-fdio.h | 13 +++++++++++- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 42fc10ab..b45b927b 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -587,7 +587,7 @@ glnx_file_copy_at (int src_dfd, } /** - * glnx_file_replace_contents_utf8_at: + * glnx_file_replace_contents_at: * @dfd: Directory fd * @subpath: Subpath * @buf: (array len=len) (element-type guint8): File contents @@ -609,10 +609,40 @@ glnx_file_replace_contents_at (int dfd, const char *subpath, const guint8 *buf, gsize len, - int mode, GLnxFileReplaceFlags flags, GCancellable *cancellable, GError **error) +{ + return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len, + (mode_t) -1, (uid_t) -1, (gid_t) -1, + flags, cancellable, error); + +} + +/** + * glnx_file_replace_contents_with_perms_at: + * @dfd: Directory fd + * @subpath: Subpath + * @buf: (array len=len) (element-type guint8): File contents + * @len: Length (if `-1`, assume @buf is `NUL` terminated) + * @flags: Flags + * @cancellable: Cancellable + * @error: Error + * + * Like glnx_file_replace_contents_at(), but also supports + * setting mode, and uid/gid. + */ +gboolean +glnx_file_replace_contents_with_perms_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + mode_t mode, + uid_t uid, + gid_t gid, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; /* We use the /proc/self trick as there's no mkostemp_at() yet */ @@ -625,12 +655,6 @@ glnx_file_replace_contents_at (int dfd, goto out; } - if (fchmod (fd, mode) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } - if (len == -1) len = strlen ((char*)buf); @@ -673,6 +697,24 @@ glnx_file_replace_contents_at (int dfd, } } + if (uid != (uid_t) -1) + { + if (fchown (fd, uid, gid) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + if (mode != (mode_t) -1) + { + if (fchmod (fd, mode) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + if (renameat (dfd, tmppath, dfd, subpath) != 0) { glnx_set_error_from_errno (error); diff --git a/glnx-fdio.h b/glnx-fdio.h index a380c1f8..a90544a9 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -80,11 +80,22 @@ glnx_file_replace_contents_at (int dfd, const char *subpath, const guint8 *buf, gsize len, - int mode, GLnxFileReplaceFlags flags, GCancellable *cancellable, GError **error); +gboolean +glnx_file_replace_contents_with_perms_at (int dfd, + const char *subpath, + const guint8 *buf, + gsize len, + mode_t mode, + uid_t uid, + gid_t gid, + GLnxFileReplaceFlags flags, + GCancellable *cancellable, + GError **error); + char * glnx_readlinkat_malloc (int dfd, const char *subpath, From 20fc302df59ea7ca928756b8221faede31bcbab4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 13 Apr 2015 13:35:32 -0400 Subject: [PATCH 032/280] fdio: Canonicalize dfd for replace contents API Just noticed while inspecting the code. --- glnx-fdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index b45b927b..db214dd5 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -649,6 +649,8 @@ glnx_file_replace_contents_with_perms_at (int dfd, g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd); glnx_fd_close int fd = -1; + dfd = glnx_dirfd_canonicalize (dfd); + if ((fd = mkostemp (tmppath, O_CLOEXEC)) == -1) { glnx_set_error_from_errno (error); From 02af92ffefda4251d894f0ec7a3aff9bc26af1cd Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 14 Apr 2015 08:43:37 -0400 Subject: [PATCH 033/280] fdio: Fix default mode in glnx_file_replace_contents_with_perms_at() mkostemp() defaults to 0600. Use 0644 instead unless a mode is explicitly provided. https://bugzilla.gnome.org/747813 --- glnx-fdio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index db214dd5..a5297943 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -708,13 +708,13 @@ glnx_file_replace_contents_with_perms_at (int dfd, } } - if (mode != (mode_t) -1) + if (mode == (mode_t) -1) + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + + if (fchmod (fd, mode) != 0) { - if (fchmod (fd, mode) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + glnx_set_error_from_errno (error); + goto out; } if (renameat (dfd, tmppath, dfd, subpath) != 0) From c36ea3ea80cdf4693005c99315dae0e2e4edc541 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 14 Apr 2015 08:27:23 -0400 Subject: [PATCH 034/280] fdio: Honor umask by default for glnx_file_replace_contents_at() By default (with `-1) use the Unix traditional default of `0666 - umask`. But do support forcing a mode. --- glnx-fdio.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index a5297943..ca2c0106 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -596,13 +596,14 @@ glnx_file_copy_at (int src_dfd, * @cancellable: Cancellable * @error: Error * - * Atomically replace the contents of @subpath (relative to @dfd) with - * @buf. By default, if the file already existed, fdatasync() will be - * used before rename() to ensure stable contents. This and other - * behavior can be controlled via @flags. + * Create a new file, atomically replacing the contents of @subpath + * (relative to @dfd) with @buf. By default, if the file already + * existed, fdatasync() will be used before rename() to ensure stable + * contents. This and other behavior can be controlled via @flags. * * Note that no metadata from the existing file is preserved, such as - * uid/gid or extended attributes. + * uid/gid or extended attributes. The default mode will be `0666`, + * modified by umask. */ gboolean glnx_file_replace_contents_at (int dfd, @@ -625,6 +626,7 @@ glnx_file_replace_contents_at (int dfd, * @subpath: Subpath * @buf: (array len=len) (element-type guint8): File contents * @len: Length (if `-1`, assume @buf is `NUL` terminated) + * @mode: File mode; if `-1`, use `0666 - umask` * @flags: Flags * @cancellable: Cancellable * @error: Error @@ -651,7 +653,8 @@ glnx_file_replace_contents_with_perms_at (int dfd, dfd = glnx_dirfd_canonicalize (dfd); - if ((fd = mkostemp (tmppath, O_CLOEXEC)) == -1) + if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC, + mode == (mode_t) -1 ? 0666 : mode)) == -1) { glnx_set_error_from_errno (error); goto out; @@ -708,13 +711,14 @@ glnx_file_replace_contents_with_perms_at (int dfd, } } - if (mode == (mode_t) -1) - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - - if (fchmod (fd, mode) != 0) + /* If a mode was forced, override umask */ + if (mode != (mode_t) -1) { - glnx_set_error_from_errno (error); - goto out; + if (fchmod (fd, mode) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } } if (renameat (dfd, tmppath, dfd, subpath) != 0) From 371172bcfd869867cf1c2847fcbbb3aa22adddb6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 14 Apr 2015 08:40:42 -0400 Subject: [PATCH 035/280] fdio: Fix errno handling from posix_fallocate() and loop_write() For extra fun, both of these functions have different error handling schemes. `posix_fallocate` does *not* set `errno` because... I'm not sure. Maybe POSIX was trying a new function design? `loop_write` uses the systemd error handling style which returns `-errno`, so we need to set errno back so that the macro can propagate it into the `GError`. --- glnx-fdio.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index ca2c0106..a4362094 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -647,6 +647,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, GError **error) { gboolean ret = FALSE; + int r; /* We use the /proc/self trick as there's no mkostemp_at() yet */ g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd); glnx_fd_close int fd = -1; @@ -663,14 +664,18 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (len == -1) len = strlen ((char*)buf); - if (posix_fallocate (fd, 0, len)) + /* Note that posix_fallocate does *not* set errno but returns it. */ + r = posix_fallocate (fd, 0, len); + if (r != 0) { + errno = r; glnx_set_error_from_errno (error); goto out; } - if (loop_write (fd, buf, len) != 0) + if ((r = loop_write (fd, buf, len)) != 0) { + errno = -r; glnx_set_error_from_errno (error); goto out; } From c231a3b845981d4b237e8f63ef3503e070009386 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 16 Apr 2015 10:46:25 -0400 Subject: [PATCH 036/280] console: Do basic output if we're not on a tty Doing nothing isn't super useful; if you're using e.g. rpm-ostree in Jenkins you want to see *something* from the "live tail". This is a basic line-per-change implementation. Closes: https://github.com/GNOME/libglnx/pull/6 --- glnx-console.c | 53 +++++++++++++++++++++++++++++++------------------- glnx-console.h | 1 + 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index bf77b565..deb4c86c 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -140,25 +140,27 @@ glnx_console_lock (GLnxConsoleRef *console) { static gsize sigwinch_initialized = 0; - if (!stdout_is_tty ()) - return; - g_return_if_fail (!locked); g_return_if_fail (!console->locked); + console->is_tty = stdout_is_tty (); + locked = console->locked = TRUE; current_percent = 0; - if (g_once_init_enter (&sigwinch_initialized)) + if (console->is_tty) { - signal (SIGWINCH, on_sigwinch); - g_once_init_leave (&sigwinch_initialized, 1); + if (g_once_init_enter (&sigwinch_initialized)) + { + signal (SIGWINCH, on_sigwinch); + g_once_init_leave (&sigwinch_initialized, 1); + } + + { static const char initbuf[] = { '\n', 0x1B, 0x37 }; + (void) fwrite (initbuf, 1, sizeof (initbuf), stdout); + } } - - { static const char initbuf[] = { '\n', 0x1B, 0x37 }; - (void) fwrite (initbuf, 1, sizeof (initbuf), stdout); - } } static void @@ -180,13 +182,13 @@ printpad (const char *padbuf, * @text: Show this text before the progress bar * @percentage: An integer in the range of 0 to 100 * - * Print to the console @text followed by an ASCII art progress bar - * whose percentage is @percentage. + * On a tty, print to the console @text followed by an ASCII art + * progress bar whose percentage is @percentage. If stdout is not a + * tty, a more basic line by line change will be printed. * * You must have called glnx_console_lock() before invoking this * function. * - * Currently, if stdout is not a tty, this function does nothing. */ void glnx_console_progress_text_percent (const char *text, @@ -202,9 +204,6 @@ glnx_console_progress_text_percent (const char *text, guint textlen; guint barlen; - if (!stdout_is_tty ()) - return; - g_return_if_fail (percentage >= 0 && percentage <= 100); if (text && !*text) @@ -214,6 +213,21 @@ glnx_console_progress_text_percent (const char *text, && g_strcmp0 (text, current_text) == 0) return; + if (!stdout_is_tty ()) + { + if (text) + fprintf (stdout, "%s", text); + if (percentage != -1) + { + if (text) + fputc (' ', stdout); + fprintf (stdout, "%u%%", percentage); + } + fputc ('\n', stdout); + fflush (stdout); + return; + } + if (ncolumns < bar_min) return; /* TODO: spinner */ @@ -262,15 +276,14 @@ glnx_console_progress_text_percent (const char *text, void glnx_console_unlock (GLnxConsoleRef *console) { - if (!stdout_is_tty ()) - return; - g_return_if_fail (locked); g_return_if_fail (console->locked); current_percent = -1; g_clear_pointer (¤t_text, g_free); - fputc ('\n', stdout); + if (console->is_tty) + fputc ('\n', stdout); + locked = FALSE; } diff --git a/glnx-console.h b/glnx-console.h index 9e259a52..9f620cc7 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS struct GLnxConsoleRef { gboolean locked; + gboolean is_tty; }; typedef struct GLnxConsoleRef GLnxConsoleRef; From 381ca54ee3a47de291d26a5db8772732fb4a9d59 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 17 Apr 2015 09:30:03 -0400 Subject: [PATCH 037/280] dirfd: Add API to get an absolute path from a dfd/relpath There are a lot of APIs that still only take absolute paths, such as librpm (and everything above it). I plan to use this in rpm-ostree to convert temporary directories that I'm accessing fd-relative back into absolutes until such time as fd-relative APIs are plumbed through the stack more. --- glnx-dirfd.c | 22 ++++++++++++++++++++++ glnx-dirfd.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 4b7d5a04..a51eb3de 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -204,3 +204,25 @@ glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) (void) closedir (real_dfd_iter->d); real_dfd_iter->initialized = FALSE; } + +/** + * glnx_fdrel_abspath: + * @dfd: Directory fd + * @path: Path + * + * Turn a fd-relative pair into something that can be used for legacy + * APIs expecting absolute paths. + * + * This is Linux specific, and only valid inside this process (unless + * you set up the child process to have the exact same fd number, but + * don't try that). + */ +char * +glnx_fdrel_abspath (int dfd, + const char *path) +{ + dfd = glnx_dirfd_canonicalize (dfd); + if (dfd == AT_FDCWD) + return g_strdup (path); + return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path); +} diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 585d16af..bcf7f1e7 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -74,4 +74,7 @@ gboolean glnx_opendirat (int dfd, int *out_fd, GError **error); +char *glnx_fdrel_abspath (int dfd, + const char *path); + G_END_DECLS From 90390949f209b373f970888d1b370c2fcba0c092 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 20 Apr 2015 22:10:07 -0400 Subject: [PATCH 038/280] fdio: glnx_file_copy_at: If no stbuf passed, do stat internally There are some cases where we want to copy with just a filename, so let's be convenient in that case and do stat for the caller. This will be used by an ostree commit. --- glnx-fdio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index a4362094..517f1e54 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -484,6 +484,7 @@ glnx_file_copy_at (int src_dfd, struct timespec ts[2]; glnx_fd_close int src_fd = -1; glnx_fd_close int dest_fd = -1; + struct stat local_stbuf; if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; @@ -491,6 +492,17 @@ glnx_file_copy_at (int src_dfd, src_dfd = glnx_dirfd_canonicalize (src_dfd); dest_dfd = glnx_dirfd_canonicalize (dest_dfd); + /* Automatically do stat() if no stat buffer was supplied */ + if (!src_stbuf) + { + if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + src_stbuf = &local_stbuf; + } + if (S_ISLNK (src_stbuf->st_mode)) { return copy_symlink_at (src_dfd, src_subpath, src_stbuf, From dfe77be2d558373c4b01189e2048d79be9c9c453 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 4 May 2015 11:57:11 -0400 Subject: [PATCH 039/280] Backport more autocleanup types from GLib GStrv, GFile, GOutputStream --- glnx-backport-autocleanups.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index daa89a9b..f616039f 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -64,6 +64,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear) @@ -78,4 +79,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) +/* Add GObject-based types add needed. */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) + #endif From fda8f26625ee0e36d89e8c54fa6ca6bd75a47aa5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 4 May 2015 16:10:28 -0400 Subject: [PATCH 040/280] README.md: Some more rationale --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 5627c6af..b1f492f2 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,32 @@ libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem It is for modules which depend on both GLib and Linux, intended to be used as a git submodule. +Features: + + - File APIs which use `openat()` like APIs, but also take a `GCancellable` + to support dynamic cancellation + - APIs also have a `GError` parameter + - High level "shutil", somewhat inspired by Python's + - A "console" API for tty output + - Some basic container utility functions + - A backport of the GLib cleanup macros for projects which can't yet take + a dependency on 2.40. + +Why? +---- + +There are multiple projects which have a hard dependency on Linux and +GLib, such as NetworkManager, ostree, xdg-app, etc. It makes sense +for them to be able to share Linux-specific APIs. + +This module also contains some code taken from systemd, which has very +high quality LGPLv2+ shared library code, but most of the internal +shared library is private, and not namespaced. + +One could also compare this project to gnulib; the salient differences +there are that at least some of this module is eventually destined for +inclusion in GLib. + Porting from libgsystem ----------------------- From be6bc2d75d390959f24fcc53edf3f4ddfdcddbaf Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 4 May 2015 16:42:59 -0400 Subject: [PATCH 041/280] lockfile: New code to lock files, massaged from systemd Will be used by OSTree to lock the sysroot against concurrent manipulation. Taken from current systemd git master, tweaked to GLibify. --- Makefile-libglnx.am | 2 + glnx-lockfile.c | 193 ++++++++++++++++++++++++++++++++++++++++++++ glnx-lockfile.h | 41 ++++++++++ libglnx.h | 1 + 4 files changed, 237 insertions(+) create mode 100644 glnx-lockfile.c create mode 100644 glnx-lockfile.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 9ccceeb8..c05a5a42 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -30,6 +30,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-dirfd.c \ $(libglnx_srcpath)/glnx-fdio.h \ $(libglnx_srcpath)/glnx-fdio.c \ + $(libglnx_srcpath)/glnx-lockfile.h \ + $(libglnx_srcpath)/glnx-lockfile.c \ $(libglnx_srcpath)/glnx-libcontainer.h \ $(libglnx_srcpath)/glnx-libcontainer.c \ $(libglnx_srcpath)/glnx-xattrs.h \ diff --git a/glnx-lockfile.c b/glnx-lockfile.c new file mode 100644 index 00000000..be9ad3a0 --- /dev/null +++ b/glnx-lockfile.c @@ -0,0 +1,193 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + Now copied into libglnx: + - Use GError + + Copyright 2010 Lennart Poettering + Copyright 2015 Colin Walters + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glnx-lockfile.h" +#include "glnx-errors.h" +#include "glnx-fdio.h" +#include "glnx-backport-autocleanups.h" +#include "glnx-local-alloc.h" + +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) + +/** + * glnx_make_lock_file: + * @dfd: Directory file descriptor (if not `AT_FDCWD`, must have lifetime `>=` @out_lock) + * @p: Path + * @operation: one of `LOCK_SH`, `LOCK_EX`, `LOCK_UN`, as passed to flock() + * @out_lock: (out) (caller allocates): Return location for lock + * @error: Error + * + * Block until a lock file named @p (relative to @dfd) can be created, + * using the flags in @operation, returning the lock data in the + * caller-allocated location @out_lock. + * + * This API wraps new-style process locking if available, otherwise + * falls back to BSD locks. + */ +gboolean +glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) { + gboolean ret = FALSE; + glnx_fd_close int fd = -1; + g_autofree char *t = NULL; + int r; + + /* + * We use UNPOSIX locks if they are available. They have nice + * semantics, and are mostly compatible with NFS. However, + * they are only available on new kernels. When we detect we + * are running on an older kernel, then we fall back to good + * old BSD locks. They also have nice semantics, but are + * slightly problematic on NFS, where they are upgraded to + * POSIX locks, even though locally they are orthogonal to + * POSIX locks. + */ + + t = g_strdup(p); + + for (;;) { +#ifdef F_OFD_SETLK + struct flock fl = { + .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, + .l_whence = SEEK_SET, + }; +#endif + struct stat st; + + fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + glnx_set_error_from_errno(error); + goto out; + } + + /* Unfortunately, new locks are not in RHEL 7.1 glibc */ +#ifdef F_OFD_SETLK + r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); +#else + r = -1; + errno = EINVAL; +#endif + if (r < 0) { + + /* If the kernel is too old, use good old BSD locks */ + if (errno == EINVAL) + r = flock(fd, operation); + + if (r < 0) { + glnx_set_error_from_errno(error); + goto out; + } + } + + /* If we acquired the lock, let's check if the file + * still exists in the file system. If not, then the + * previous exclusive owner removed it and then closed + * it. In such a case our acquired lock is worthless, + * hence try again. */ + + r = fstat(fd, &st); + if (r < 0) { + glnx_set_error_from_errno(error); + goto out; + } + if (st.st_nlink > 0) + break; + + (void) close(fd); + fd = -1; + } + + /* Note that if this is not AT_FDCWD, the caller takes responsibility + * for the fd's lifetime being >= that of the lock. + */ + out_lock->dfd = dfd; + out_lock->path = t; + out_lock->fd = fd; + out_lock->operation = operation; + + fd = -1; + t = NULL; + + ret = TRUE; + out: + return ret; +} + +void glnx_release_lock_file(GLnxLockFile *f) { + int r; + + if (!f) + return; + + if (f->path) { + + /* If we are the exclusive owner we can safely delete + * the lock file itself. If we are not the exclusive + * owner, we can try becoming it. */ + + if (f->fd >= 0 && + (f->operation & ~LOCK_NB) == LOCK_SH) { +#ifdef F_OFD_SETLK + static const struct flock fl = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + }; + + r = fcntl(f->fd, F_OFD_SETLK, &fl); +#else + r = -1; + errno = EINVAL; +#endif + if (r < 0 && errno == EINVAL) + r = flock(f->fd, LOCK_EX|LOCK_NB); + + if (r >= 0) + f->operation = LOCK_EX|LOCK_NB; + } + + if ((f->operation & ~LOCK_NB) == LOCK_EX) { + (void) unlinkat(f->dfd, f->path, 0); + } + + g_free(f->path); + f->path = NULL; + } + + (void) close (f->fd); + f->fd = -1; + f->operation = 0; +} diff --git a/glnx-lockfile.h b/glnx-lockfile.h new file mode 100644 index 00000000..6d132e5f --- /dev/null +++ b/glnx-lockfile.h @@ -0,0 +1,41 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + Copyright 2015 Colin Walters + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +#include "config.h" + +#include "glnx-backport-autoptr.h" + +typedef struct GLnxLockFile { + int dfd; + char *path; + int fd; + int operation; +} GLnxLockFile; + +gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *ret, GError **error); +void glnx_release_lock_file(GLnxLockFile *f); + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxLockFile, glnx_release_lock_file) + +#define GLNX_LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL } diff --git a/libglnx.h b/libglnx.h index 89defac1..ef52c245 100644 --- a/libglnx.h +++ b/libglnx.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #include #include +#include #include #include #include From 013aa4feadb1fb7f2dd361b73f7d0fd623ae2daa Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Tue, 5 May 2015 10:26:33 -0400 Subject: [PATCH 042/280] Backport all the GIO object types used by OSTree --- glnx-backport-autocleanups.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index f616039f..6bd2f902 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -80,7 +80,23 @@ G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) /* Add GObject-based types add needed. */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileEnumerator, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) #endif From cf8ae27bab717621f1edf8ab6ae3dd89da4d8d4f Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 5 May 2015 16:37:24 +0200 Subject: [PATCH 043/280] glnx-fdio: always initialize buf It can be used without initialization if condition causing a "goto error" fails before buf is initialized. Signed-off-by: Giuseppe Scrivano --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 517f1e54..7db33c4c 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -211,7 +211,7 @@ glnx_file_get_contents_utf8_at (int dfd, { gboolean success = FALSE; glnx_fd_close int fd = -1; - char *buf; + char *buf = NULL; gsize len; dfd = glnx_dirfd_canonicalize (dfd); From 900b25f7018878ab64bd04751d8f15c6d83ba823 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 8 May 2015 22:02:44 -0400 Subject: [PATCH 044/280] backport-autocleanups: GTask For OSTree. --- glnx-backport-autocleanups.h | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 6bd2f902..05b405c5 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -95,6 +95,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) From 91875459cdc9e61c2ea8c5831c7593ec7641fd2b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 15 Jun 2015 12:27:30 -0400 Subject: [PATCH 045/280] libcontainer: Search $PATH for exec() if argv[0] is not absolute In Fedora rawhide, dracut switched from `/usr/sbin` to `/usr/bin`, which broke rpm-ostree's hardcoding of the path. There was no real reason to hardcode it (assume our `$PATH` is sane and secure), so in order to help support that, this change in libglnx will automatically search $PATH if the input is not absolute. (This is a sane default for a process spawning library IMO) --- glnx-libcontainer.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index dafd8ad4..49fa0955 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -242,8 +242,16 @@ glnx_libcontainer_run_in_root (const char *dest, if (chdir ("/") != 0) _perror_fatal ("chdir: "); - if (execv (binary, argv) != 0) - _perror_fatal ("execl: "); + if (binary[0] == '/') + { + if (execv (binary, argv) != 0) + _perror_fatal ("execv: "); + } + else + { + if (execvp (binary, argv) != 0) + _perror_fatal ("execvp: "); + } g_assert_not_reached (); } From 58a9a5c50ebd8415162251ce9235835dca3f6a37 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 28 Jul 2015 16:40:29 -0400 Subject: [PATCH 046/280] libcontainer: Fall back to noop if / is not a mountpoint Trying to change the mount namespace was breaking in mock, which just uses plain chroot. --- glnx-libcontainer.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index 49fa0955..3f500073 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -212,10 +212,24 @@ glnx_libcontainer_run_in_root (const char *dest, if (container_available) { if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) - _perror_fatal ("mount: "); + { + if (errno == EINVAL) + { + /* Ok, we may be inside a mock chroot or the like. In + * that case, let's just fall back to not + * containerizing. + */ + container_available = FALSE; + } + else + _perror_fatal ("mount: "); + } - if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) - _perror_fatal ("mount (MS_NOSUID): "); + if (container_available) + { + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) + _perror_fatal ("mount (MS_NOSUID): "); + } } if (chdir (dest) != 0) From fbdb15cd958c007dc14f6e7cfe314b14c042ce3c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 24 Aug 2015 13:44:50 -0400 Subject: [PATCH 047/280] libcontainer: Pare down to just "run in root" API rpm-ostree is going through some awkwardness trying to support being run both inside and outside of a container. For now, let's drop all recursive container usage. There is still some value in readding this in the future - for example, downloading packages requires networking, but `%post` scripts do not. But we should really solve that when we return to running unprivileged or the like. --- glnx-libcontainer.c | 62 +++++++++++++++++++++++++++++---------------- glnx-libcontainer.h | 14 +++------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index 3f500073..b4e93e7f 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -37,8 +37,6 @@ #include "glnx-backport-autocleanups.h" #include "glnx-local-alloc.h" -static gboolean container_available = TRUE; - static void _perror_fatal (const char *message) __attribute__ ((noreturn)); static void @@ -48,19 +46,35 @@ _perror_fatal (const char *message) exit (1); } -void -glnx_libcontainer_set_not_available (void) -{ - container_available = FALSE; -} +typedef enum { + CONTAINER_UNINIT = 0, + CONTAINER_YES = 1, + CONTAINER_NO = 2 +} ContainerDetectionState; -gboolean -glnx_libcontainer_get_available (void) +static gboolean +currently_in_container (void) { - return container_available; + static gsize container_detected = CONTAINER_UNINIT; + + if (g_once_init_enter (&container_detected)) + { + ContainerDetectionState tmp_state = CONTAINER_NO; + struct stat stbuf; + + /* http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/ */ + if (getenv ("container") != NULL + || stat ("/.dockerinit", &stbuf) == 0) + tmp_state = CONTAINER_YES; + /* But since Docker isn't on board, yet, so... + http://stackoverflow.com/questions/23513045/how-to-check-if-a-process-is-running-inside-docker-container */ + g_once_init_leave (&container_detected, tmp_state); + } + return container_detected == CONTAINER_YES; } -gboolean +#if 0 +static gboolean glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) { gboolean ret = FALSE; @@ -88,10 +102,10 @@ glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) out: return ret; } - +#endif /* Based on code from nspawn.c */ -int +static int glnx_libcontainer_make_api_mounts (const char *dest) { typedef struct MountPoint { @@ -148,7 +162,7 @@ glnx_libcontainer_make_api_mounts (const char *dest) return 0; } -int +static int glnx_libcontainer_prep_dev (const char *dest_devdir) { glnx_fd_close int src_fd = -1; @@ -187,15 +201,19 @@ glnx_libcontainer_prep_dev (const char *dest_devdir) } pid_t -glnx_libcontainer_run_in_root (const char *dest, - const char *binary, - char **argv) +glnx_libcontainer_run_chroot_private (const char *dest, + const char *binary, + char **argv) { + /* Make most new namespaces; note our use of CLONE_NEWNET means we + * have no networking in the container root. + */ const int cloneflags = SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS; pid_t child; + gboolean in_container = currently_in_container (); - if (container_available) + if (!in_container) { if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0) return -1; @@ -209,7 +227,7 @@ glnx_libcontainer_run_in_root (const char *dest, if (child != 0) return child; - if (container_available) + if (!in_container) { if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) { @@ -219,13 +237,13 @@ glnx_libcontainer_run_in_root (const char *dest, * that case, let's just fall back to not * containerizing. */ - container_available = FALSE; + in_container = TRUE; } else _perror_fatal ("mount: "); } - if (container_available) + if (!in_container) { if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) _perror_fatal ("mount (MS_NOSUID): "); @@ -235,7 +253,7 @@ glnx_libcontainer_run_in_root (const char *dest, if (chdir (dest) != 0) _perror_fatal ("chdir: "); - if (container_available) + if (!in_container) { if (glnx_libcontainer_make_api_mounts (dest) != 0) _perror_fatal ("preparing api mounts: "); diff --git a/glnx-libcontainer.h b/glnx-libcontainer.h index 16cdb654..10855db7 100644 --- a/glnx-libcontainer.h +++ b/glnx-libcontainer.h @@ -31,14 +31,6 @@ #include #include -void glnx_libcontainer_set_not_available (void); -gboolean glnx_libcontainer_get_available (void); - -gboolean glnx_libcontainer_bind_mount_readonly (const char *path, GError **error); - -int glnx_libcontainer_make_api_mounts (const char *dest); -int glnx_libcontainer_prep_dev (const char *dest); - -pid_t glnx_libcontainer_run_in_root (const char *dest, - const char *binary, - char **argv); +pid_t glnx_libcontainer_run_chroot_private (const char *dest, + const char *binary, + char **argv); From dc5702aba93b5598808d7d7e2d65eccffa31de2c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 28 Aug 2015 13:10:01 -0400 Subject: [PATCH 048/280] backport-autocleanups: Add GDBusConnection,GDBusMessage --- glnx-backport-autocleanups.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 05b405c5..472c8656 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -99,5 +99,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref) #endif From 0cf50c6735246147d0a231d45f730724de0841e8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 29 Aug 2015 09:28:35 -0400 Subject: [PATCH 049/280] Add a generic glnx-backports.h Where we can put general functions that come in newer glib. --- Makefile-libglnx.am | 2 ++ glnx-backports.c | 41 +++++++++++++++++++++++++++++++++++++++++ glnx-backports.h | 36 ++++++++++++++++++++++++++++++++++++ libglnx.h | 1 + 4 files changed, 80 insertions(+) create mode 100644 glnx-backports.c create mode 100644 glnx-backports.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index c05a5a42..712e931c 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -20,6 +20,8 @@ EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ + $(libglnx_srcpath)/glnx-backports.h \ + $(libglnx_srcpath)/glnx-backports.c \ $(libglnx_srcpath)/glnx-local-alloc.h \ $(libglnx_srcpath)/glnx-local-alloc.c \ $(libglnx_srcpath)/glnx-errors.h \ diff --git a/glnx-backports.c b/glnx-backports.c new file mode 100644 index 00000000..075c21ac --- /dev/null +++ b/glnx-backports.c @@ -0,0 +1,41 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * This program 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 of the licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "glnx-backports.h" + +#if !GLIB_CHECK_VERSION(2, 44, 0) +gboolean +glnx_strv_contains (const gchar * const *strv, + const gchar *str) +{ + g_return_val_if_fail (strv != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + for (; *strv != NULL; strv++) + { + if (g_str_equal (str, *strv)) + return TRUE; + } + + return FALSE; +} +#endif diff --git a/glnx-backports.h b/glnx-backports.h new file mode 100644 index 00000000..825ef0f8 --- /dev/null +++ b/glnx-backports.h @@ -0,0 +1,36 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Colin Walters + * + * GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#if !GLIB_CHECK_VERSION(2, 44, 0) +#define g_strv_contains glnx_strv_contains +gboolean glnx_strv_contains (const gchar * const *strv, + const gchar *str); +#endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index ef52c245..9bd41c45 100644 --- a/libglnx.h +++ b/libglnx.h @@ -26,6 +26,7 @@ G_BEGIN_DECLS #include #include +#include #include #include #include From e684ef07f03dd563310788c90b3cdb00bac551eb Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 16 Sep 2015 14:14:43 -0400 Subject: [PATCH 050/280] libcontainer: Set PATH when using execvp() Set PATH to something sane for a chroot'ed environment rather than relying on the user's PATH value. --- glnx-libcontainer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index b4e93e7f..8c0f3407 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -281,6 +281,9 @@ glnx_libcontainer_run_chroot_private (const char *dest, } else { + /* Set PATH to something sane. */ + setenv ("PATH", "/usr/sbin:/usr/bin", 1); + if (execvp (binary, argv) != 0) _perror_fatal ("execvp: "); } From 7310bbdb58379fa4e6d7ec99aa89c2b069814335 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 24 Sep 2015 15:28:21 +0200 Subject: [PATCH 051/280] Backport g_autoptr support for GString --- glnx-backport-autocleanups.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 472c8656..f2bc4836 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -31,6 +31,13 @@ g_autoptr_cleanup_generic_gfree (void *p) g_free (*pp); } +static inline void +g_autoptr_cleanup_gstring_free (GString *string) +{ + if (string) + g_string_free (string, TRUE); +} + G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref) @@ -48,6 +55,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) From c7da81208df4053774cb4b38adff8863ca0838f5 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 6 Nov 2015 09:57:30 -0500 Subject: [PATCH 052/280] Backport g_autoptr support for GCancellable --- glnx-backport-autocleanups.h | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index f2bc4836..271f5af9 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -88,6 +88,7 @@ G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) /* Add GObject-based types add needed. */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref) From 825d308b04771096329c6d29772ee1243f4cb2f4 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 6 Nov 2015 10:31:37 -0500 Subject: [PATCH 053/280] Comment typo --- glnx-backport-autocleanups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 271f5af9..ee67ba2f 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -87,7 +87,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) -/* Add GObject-based types add needed. */ +/* Add GObject-based types as needed. */ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref) From 7e24c244ffb3c1b7825c9e45dbf2806b86e46f95 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 23 Nov 2015 14:47:34 -0500 Subject: [PATCH 054/280] Backport g_autoptr support for GFileOutputStream --- glnx-backport-autocleanups.h | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index ee67ba2f..06c76cfe 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -98,6 +98,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref) From dade12b5587e14058ad93263ee744f91038de68c Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Dec 2015 16:23:27 +0100 Subject: [PATCH 055/280] Add g_autoptr support for GSubprocess --- glnx-backport-autocleanups.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 06c76cfe..53f7ffd2 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -86,6 +86,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref) /* Add GObject-based types as needed. */ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref) From 1d65bc0ff740e4eb44610f9b699bf6a05e799493 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 1 Dec 2015 16:23:56 +0100 Subject: [PATCH 056/280] Add backport of g_set_object --- glnx-backports.c | 20 ++++++++++++++++++++ glnx-backports.h | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/glnx-backports.c b/glnx-backports.c index 075c21ac..c7bb600f 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -38,4 +38,24 @@ glnx_strv_contains (const gchar * const *strv, return FALSE; } + +gboolean +glnx_set_object (GObject **object_ptr, + GObject *new_object) +{ + GObject *old_object = *object_ptr; + + if (old_object == new_object) + return FALSE; + + if (new_object != NULL) + g_object_ref (new_object); + + *object_ptr = new_object; + + if (old_object != NULL) + g_object_unref (old_object); + + return TRUE; +} #endif diff --git a/glnx-backports.h b/glnx-backports.h index 825ef0f8..cd853cca 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -28,9 +28,19 @@ G_BEGIN_DECLS #if !GLIB_CHECK_VERSION(2, 44, 0) + #define g_strv_contains glnx_strv_contains gboolean glnx_strv_contains (const gchar * const *strv, const gchar *str); + +#define g_set_object(object_ptr, new_object) \ + (/* Check types match. */ \ + 0 ? *(object_ptr) = (new_object), FALSE : \ + glnx_set_object ((GObject **) (object_ptr), (GObject *) (new_object)) \ + ) +gboolean glnx_set_object (GObject **object_ptr, + GObject *new_object); + #endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ G_END_DECLS From bf4d1504a40cdb2b1a90ed2eb6366ec28e60bb68 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 2 Dec 2015 09:58:18 -0500 Subject: [PATCH 057/280] README.md: Add note on contributing --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b1f492f2..50a5f90d 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,12 @@ APIs not defined in GLib yet, such as `glnx_fd_close`. `gs_transfer_out_value` is replaced by `g_steal_pointer`. +Contributing +------------ + +Currently there is not a Bugzilla product - one may be created +in the future. You can submit PRs against the Github mirror: + +https://github.com/GNOME/libglnx/pulls + +Or alternatively, email one of the maintainers directly. From 4e9969998b3d385fc007fa5deba5f256aa7b2e95 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 11 Dec 2015 15:56:06 +0100 Subject: [PATCH 058/280] Add glnx_steal_fd This is very useful in combination with glnx_close_fd https://bugzilla.gnome.org/show_bug.cgi?id=757611 --- glnx-local-alloc.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index a193e603..af5af4b8 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -211,4 +211,12 @@ glnx_cleanup_close_fdp (int *fdp) */ #define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp))) +static inline int +glnx_steal_fd (int *fdp) +{ + int fd = *fdp; + *fdp = -1; + return fd; +} + G_END_DECLS From 03138641298fd6799f46b16423871f959332bacf Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 11 Dec 2015 15:56:29 +0100 Subject: [PATCH 059/280] Add glnx_mkdtempat Create a temporary directory using mkdirat. https://bugzilla.gnome.org/show_bug.cgi?id=757611 --- glnx-dirfd.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-dirfd.h | 5 ++++ 2 files changed, 82 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index a51eb3de..d126f15a 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -226,3 +226,80 @@ glnx_fdrel_abspath (int dfd, return g_strdup (path); return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path); } + +/** + * glnx_mkdtempat: + * @dfd: Directory fd + * @tmpl: (type filename): template directory name + * @mode: permissions to create the temporary directory with + * @error: Error + * + * Similar to g_mkdtemp_full, but using openat. + */ +gboolean +glnx_mkdtempat (int dfd, + gchar *tmpl, + int mode, + GError **error) +{ + char *XXXXXX; + int count; + static const char letters[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const int NLETTERS = sizeof (letters) - 1; + glong value; + GTimeVal tv; + static int counter = 0; + + g_return_val_if_fail (tmpl != NULL, -1); + + /* find the last occurrence of "XXXXXX" */ + XXXXXX = g_strrstr (tmpl, "XXXXXX"); + + if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "Invalid temporary directory template '%s'", tmpl); + return FALSE; + } + + /* Get some more or less random data. */ + g_get_current_time (&tv); + value = (tv.tv_usec ^ tv.tv_sec) + counter++; + + for (count = 0; count < 100; value += 7777, ++count) + { + glong v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[1] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[2] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[3] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[4] = letters[v % NLETTERS]; + v /= NLETTERS; + XXXXXX[5] = letters[v % NLETTERS]; + + if (mkdirat (dfd, tmpl, mode) == -1) + { + if (errno == EEXIST) + continue; + + /* Any other error will apply also to other names we might + * try, and there are 2^32 or so of them, so give up now. + */ + glnx_set_prefix_error_from_errno (error, "%s", "mkdirat"); + return FALSE; + } + + return TRUE; + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "mkstempat ran out of combinations to try."); + return FALSE; +} diff --git a/glnx-dirfd.h b/glnx-dirfd.h index bcf7f1e7..5b7b77a9 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -77,4 +77,9 @@ gboolean glnx_opendirat (int dfd, char *glnx_fdrel_abspath (int dfd, const char *path); +gboolean glnx_mkdtempat (int dfd, + gchar *tmpl, + int mode, + GError **error); + G_END_DECLS From e7f7081054311670411b8ad34f144c99822538c1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 17 Dec 2015 13:29:08 +0100 Subject: [PATCH 060/280] autocleanups: Fix g_autoptr(GString) with glib 2.44 The g_autoptr support for GString was added in 2.45.8, so we need to define it on 2.44 for it to work. --- glnx-backport-autocleanups.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 53f7ffd2..9e90fa87 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -31,13 +31,6 @@ g_autoptr_cleanup_generic_gfree (void *p) g_free (*pp); } -static inline void -g_autoptr_cleanup_gstring_free (GString *string) -{ - if (string) - g_string_free (string, TRUE); -} - G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref) @@ -55,7 +48,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) @@ -115,3 +107,16 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref) #endif + +#if !GLIB_CHECK_VERSION(2, 45, 8) + +static inline void +g_autoptr_cleanup_gstring_free (GString *string) +{ + if (string) + g_string_free (string, TRUE); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free) + +#endif From 91e060699f5559c49335ce19f4b23ba70dfd6bb3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 1 Jan 2016 17:26:12 -0500 Subject: [PATCH 061/280] dirfd: Add a public API to ensure a filled dtype It's quite common to iterate over a directory recursively, only caring about the file type, but not other bits returned by `stat()`. Good file systems fill in `dt_type`, but not all do. This function papers over that in userspace conveniently. --- glnx-dirfd.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ glnx-dirfd.h | 4 ++++ 2 files changed, 53 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index d126f15a..4861ccfe 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -168,6 +168,8 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, gboolean ret = FALSE; GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + g_return_val_if_fail (out_dent, FALSE); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; @@ -189,6 +191,53 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, return ret; } +/** + * glnx_dirfd_iterator_next_dent_ensure_dtype: + * @dfd_iter: A directory iterator + * @out_dent: (out) (transfer none): Pointer to dirent; do not free + * @cancellable: Cancellable + * @error: Error + * + * A variant of @glnx_dirfd_iterator_next_dent, which will ensure the + * `dent->d_type` member is filled in by calling `fstatat` + * automatically if the underlying filesystem type sets `DT_UNKNOWN`. + */ +gboolean +glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + struct dirent *ret_dent; + + g_return_val_if_fail (out_dent, FALSE); + + if (!glnx_dirfd_iterator_next_dent (dfd_iter, out_dent, cancellable, error)) + goto out; + + ret_dent = *out_dent; + + if (ret_dent) + { + + if (ret_dent->d_type == DT_UNKNOWN) + { + struct stat stbuf; + if (TEMP_FAILURE_RETRY (fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) + { + glnx_set_error_from_errno (error); + goto out; + } + ret_dent->d_type = IFTODT (stbuf.st_mode); + } + } + + ret = TRUE; + out: + return ret; +} + /** * glnx_dirfd_iterator_clear: * @dfd_iter: Iterator, will be de-initialized diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 5b7b77a9..c13e3dc5 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -60,6 +60,10 @@ gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, struct dirent **out_dent, GCancellable *cancellable, GError **error); +gboolean glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, + struct dirent **out_dent, + GCancellable *cancellable, + GError **error); void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear) From 3c470803d08477dab9c7faada29a5f4c59dd519e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 10 Jan 2016 21:46:45 -0500 Subject: [PATCH 062/280] fdio: Export loop_write I plan to use this in rpm-ostree. Sad how many times this gets reinvented. Should probably stick a copy in `glib-unix.h` or so. --- glnx-fdio.c | 6 +++--- glnx-fdio.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 7db33c4c..466cbc4e 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -354,7 +354,7 @@ static int btrfs_reflink(int infd, int outfd) { return 0; } -static int loop_write(int fd, const void *buf, size_t nbytes) { +int glnx_loop_write(int fd, const void *buf, size_t nbytes) { const uint8_t *p = buf; g_return_val_if_fail(fd >= 0, -1); @@ -437,7 +437,7 @@ static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { if (n == 0) /* EOF */ break; - r = loop_write(fdt, buf, (size_t) n); + r = glnx_loop_write(fdt, buf, (size_t) n); if (r < 0) return r; } @@ -685,7 +685,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, goto out; } - if ((r = loop_write (fd, buf, len)) != 0) + if ((r = glnx_loop_write (fd, buf, len)) != 0) { errno = -r; glnx_set_error_from_errno (error); diff --git a/glnx-fdio.h b/glnx-fdio.h index a90544a9..c0fd4e48 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -102,6 +102,9 @@ glnx_readlinkat_malloc (int dfd, GCancellable *cancellable, GError **error); +int +glnx_loop_write (int fd, const void *buf, size_t nbytes); + typedef enum { GLNX_FILE_COPY_OVERWRITE, GLNX_FILE_COPY_NOXATTRS, From 194eb7a09c8baa61e9715a117d9376d43d9a86c5 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 22 Jan 2016 15:27:11 +0100 Subject: [PATCH 063/280] Add autoptr support for GZlib* --- glnx-backport-autocleanups.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 9e90fa87..cc249611 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -105,6 +105,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibCompressor, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibDecompressor, g_object_unref) #endif From 2ca280f0124eddcac8aa868fdbeb679bb6e57139 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 24 Jan 2016 10:06:24 -0500 Subject: [PATCH 064/280] Introduce glnx-alloca.h with glnx_strjoina() This is taken from systemd, and is really useful when one has a few known-to-be-small strings one wants to concatenate without resorting to malloc. --- Makefile-libglnx.am | 1 + glnx-alloca.h | 47 +++++++++++++++++++++++++++++++++++++++++++++ libglnx.h | 1 + 3 files changed, 49 insertions(+) create mode 100644 glnx-alloca.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 712e931c..2e8da015 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -18,6 +18,7 @@ EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING libglnx_la_SOURCES = \ + $(libglnx_srcpath)/glnx-alloca.h \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ $(libglnx_srcpath)/glnx-backports.h \ diff --git a/glnx-alloca.h b/glnx-alloca.h new file mode 100644 index 00000000..b0bf6a65 --- /dev/null +++ b/glnx-alloca.h @@ -0,0 +1,47 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters . + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h + * at revision v228-666-gcf6c8c4 + */ +#define glnx_strjoina(a, ...) \ + ({ \ + const char *_appendees_[] = { a, __VA_ARGS__ }; \ + char *_d_, *_p_; \ + int _len_ = 0; \ + unsigned _i_; \ + for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ + _len_ += strlen(_appendees_[_i_]); \ + _p_ = _d_ = alloca(_len_ + 1); \ + for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ + _p_ = stpcpy(_p_, _appendees_[_i_]); \ + *_p_ = 0; \ + _d_; \ + }) + + +G_END_DECLS diff --git a/libglnx.h b/libglnx.h index 9bd41c45..451740d2 100644 --- a/libglnx.h +++ b/libglnx.h @@ -24,6 +24,7 @@ G_BEGIN_DECLS +#include #include #include #include From aac5a6cef72caa7cb26092db2923b70223005c6d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 25 Jan 2016 10:23:34 -0500 Subject: [PATCH 065/280] console: g_auto() macro no-ops if console is not locked Otherwise we miss a lot of the ergonomics of cleanup macros. --- glnx-console.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-console.h b/glnx-console.h index 9f620cc7..8fc38656 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -45,7 +45,8 @@ guint glnx_console_columns (void); static inline void glnx_console_ref_cleanup (GLnxConsoleRef *p) { - glnx_console_unlock (p); + if (p->locked) + glnx_console_unlock (p); } G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup) From 34a96c03dd34636e654e8258b94da904d2923878 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 25 Jan 2016 10:23:34 -0500 Subject: [PATCH 066/280] console: Fix g_auto() and unlock cleanup This way, one can unlock the console while still using the cleanup macro. Otherwise we miss a lot of the ergonomics of cleanup macros. --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index deb4c86c..0911eaff 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -285,5 +285,5 @@ glnx_console_unlock (GLnxConsoleRef *console) if (console->is_tty) fputc ('\n', stdout); - locked = FALSE; + locked = console->locked = FALSE; } From 769522753c25537e520adc322fa62e5390272add Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 26 Jan 2016 11:16:56 -0500 Subject: [PATCH 067/280] console: Don't delete last character of output text Not sure why we were doing this...I guess people were working around it by adding their own spaces? --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 0911eaff..39733118 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -241,7 +241,7 @@ glnx_console_progress_text_percent (const char *text, if (textlen > 0) { - fwrite (text, 1, textlen - 1, stdout); + fwrite (text, 1, textlen, stdout); fputc (' ', stdout); } From 8a7943fef6061a4e9ca368e0042a8a3924affb99 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 8 Feb 2016 14:08:09 +0100 Subject: [PATCH 068/280] console: Fix bar progress length The previous fix added the last character of text, but failed to account for the space we're adding. --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 39733118..f9d8baa4 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -237,7 +237,7 @@ glnx_console_progress_text_percent (const char *text, } textlen = MIN (input_textlen, ncolumns - bar_min); - barlen = ncolumns - textlen; + barlen = ncolumns - (textlen + 1); if (textlen > 0) { From 08ae6639e522e9b11765245fbecdbbe474ccde98 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 10 Mar 2016 13:51:13 -0500 Subject: [PATCH 069/280] console: Add an API to just emit text We had this internally, just need to expose it. --- glnx-console.c | 49 +++++++++++++++++++++++++++++++------------------ glnx-console.h | 2 ++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index f9d8baa4..016f62cf 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -177,22 +177,9 @@ printpad (const char *padbuf, fwrite (padbuf, 1, r, stdout); } -/** - * glnx_console_progress_text_percent: - * @text: Show this text before the progress bar - * @percentage: An integer in the range of 0 to 100 - * - * On a tty, print to the console @text followed by an ASCII art - * progress bar whose percentage is @percentage. If stdout is not a - * tty, a more basic line by line change will be printed. - * - * You must have called glnx_console_lock() before invoking this - * function. - * - */ -void -glnx_console_progress_text_percent (const char *text, - guint percentage) +static void +text_percent_internal (const char *text, + int percentage) { static const char equals[] = "===================="; const guint n_equals = sizeof (equals) - 1; @@ -204,8 +191,6 @@ glnx_console_progress_text_percent (const char *text, guint textlen; guint barlen; - g_return_if_fail (percentage >= 0 && percentage <= 100); - if (text && !*text) text = NULL; @@ -266,6 +251,34 @@ glnx_console_progress_text_percent (const char *text, fflush (stdout); } +/** + * glnx_console_progress_text_percent: + * @text: Show this text before the progress bar + * @percentage: An integer in the range of 0 to 100 + * + * On a tty, print to the console @text followed by an ASCII art + * progress bar whose percentage is @percentage. If stdout is not a + * tty, a more basic line by line change will be printed. + * + * You must have called glnx_console_lock() before invoking this + * function. + * + */ +void +glnx_console_progress_text_percent (const char *text, + guint percentage) +{ + g_return_if_fail (percentage >= 0 && percentage <= 100); + + text_percent_internal (text, percentage); +} + +void +glnx_console_text (const char *text) +{ + text_percent_internal (text, -1); +} + /** * glnx_console_unlock: * diff --git a/glnx-console.h b/glnx-console.h index 8fc38656..8c1d8115 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -33,6 +33,8 @@ typedef struct GLnxConsoleRef GLnxConsoleRef; void glnx_console_lock (GLnxConsoleRef *ref); +void glnx_console_text (const char *text); + void glnx_console_progress_text_percent (const char *text, guint percentage); From 69d8a597f768f811f7c924a784af834f637eeb20 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 10 Mar 2016 23:12:07 +0100 Subject: [PATCH 070/280] Don't touch errno in glnx_fd_close We're ignoring the result from the close, but it can still affect errno, which is bad if you use this in functions that sets errno, because errno can unexpectedly change after you've set it. --- glnx-local-alloc.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index af5af4b8..f628b61c 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -21,6 +21,7 @@ #pragma once #include +#include G_BEGIN_DECLS @@ -195,13 +196,17 @@ GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_un static inline void glnx_cleanup_close_fdp (int *fdp) { - int fd; + int fd, errsv; g_assert (fdp); fd = *fdp; if (fd != -1) - (void) close (fd); + { + errsv = errno; + (void) close (fd); + errno = errsv; + } } /** From 47ddbfa56341df3a9453854e1101e1c2f2359ddb Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 1 May 2016 14:04:37 -0400 Subject: [PATCH 071/280] console: Fix glnx_console_text Not sure if it ever worked. We need to not print the bars, etc. --- glnx-console.c | 53 +++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index 016f62cf..bbcfe734 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -188,8 +188,6 @@ text_percent_internal (const char *text, const guint ncolumns = glnx_console_columns (); const guint bar_min = 10; const guint input_textlen = text ? strlen (text) : 0; - guint textlen; - guint barlen; if (text && !*text) text = NULL; @@ -221,32 +219,39 @@ text_percent_internal (const char *text, (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout); } - textlen = MIN (input_textlen, ncolumns - bar_min); - barlen = ncolumns - (textlen + 1); - - if (textlen > 0) + if (percentage == -1) { - fwrite (text, 1, textlen, stdout); - fputc (' ', stdout); + fwrite (text, 1, input_textlen, stdout); } + else + { + const guint textlen = MIN (input_textlen, ncolumns - bar_min); + const guint barlen = ncolumns - (textlen + 1);; + + if (textlen > 0) + { + fwrite (text, 1, textlen, stdout); + fputc (' ', stdout); + } - { - const guint nbraces = 2; - const guint textpercent_len = 5; - const guint bar_internal_len = barlen - nbraces - textpercent_len; - const guint eqlen = bar_internal_len * (percentage / 100.0); - const guint spacelen = bar_internal_len - eqlen; - - fputc ('[', stdout); - printpad (equals, n_equals, eqlen); - printpad (spaces, n_spaces, spacelen); - fputc (']', stdout); - fprintf (stdout, " %3d%%", percentage); - } + { + const guint nbraces = 2; + const guint textpercent_len = 5; + const guint bar_internal_len = barlen - nbraces - textpercent_len; + const guint eqlen = bar_internal_len * (percentage / 100.0); + const guint spacelen = bar_internal_len - eqlen; + + fputc ('[', stdout); + printpad (equals, n_equals, eqlen); + printpad (spaces, n_spaces, spacelen); + fputc (']', stdout); + fprintf (stdout, " %3d%%", percentage); + } - { const guint spacelen = ncolumns - textlen - barlen; - printpad (spaces, n_spaces, spacelen); - } + { const guint spacelen = ncolumns - textlen - barlen; + printpad (spaces, n_spaces, spacelen); + } + } fflush (stdout); } From 85c9dd5c073a8c0d74c4baa2e4a94f5535984e62 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 2 May 2016 10:38:16 -0400 Subject: [PATCH 072/280] libcontainer: Always set PATH when running in new root For rpm-ostree's use we always run in a new root, so we don't want to inherit the host system's PATH. For example, NixOS uses PATH for its software namespacing, but one could be using rpm-ostree to build CentOS commits. --- glnx-libcontainer.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index 8c0f3407..38c1937f 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -274,6 +274,14 @@ glnx_libcontainer_run_chroot_private (const char *dest, if (chdir ("/") != 0) _perror_fatal ("chdir: "); + /* Environment variables like PATH in the end are distribution + * specific. The most correct thing would be to run through PAM, + * but that's a huge level of pain. We'd like to drive towards a + * standard /usr/bin (i.e. unified sbin too), but for now this is + * pretty compatible. + */ + setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); + if (binary[0] == '/') { if (execv (binary, argv) != 0) @@ -281,9 +289,6 @@ glnx_libcontainer_run_chroot_private (const char *dest, } else { - /* Set PATH to something sane. */ - setenv ("PATH", "/usr/sbin:/usr/bin", 1); - if (execvp (binary, argv) != 0) _perror_fatal ("execvp: "); } From 3d162e772db80f6664a78583268150d2e1d1d29e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 3 May 2016 17:20:43 -0400 Subject: [PATCH 073/280] fdio: Add glnx_stream_fstat Migrated from libgsystem's `gs_stream_fstat()`. It's a small function but I end up using it in OSTree a fair bit. --- glnx-fdio.c | 28 ++++++++++++++++++++++++++++ glnx-fdio.h | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 466cbc4e..62371d02 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -748,3 +748,31 @@ glnx_file_replace_contents_with_perms_at (int dfd, out: return ret; } + +/** + * glnx_stream_fstat: + * @stream: A stream containing a Unix file descriptor + * @stbuf: Memory location to write stat buffer + * @error: + * + * Some streams created via libgsystem are #GUnixInputStream; these do + * not support e.g. g_file_input_stream_query_info(). This function + * allows dropping to the raw unix fstat() call for these types of + * streams, while still conveniently wrapped with the normal GLib + * handling of @error. + */ +gboolean +glnx_stream_fstat (GFileDescriptorBased *stream, + struct stat *stbuf, + GError **error) +{ + int fd = g_file_descriptor_based_get_fd (stream); + + if (fstat (fd, stbuf) == -1) + { + glnx_set_prefix_error_from_errno (error, "%s", "fstat"); + return FALSE; + } + + return TRUE; +} diff --git a/glnx-fdio.h b/glnx-fdio.h index c0fd4e48..3ca1a660 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -21,6 +21,7 @@ #pragma once #include +#include #include #include #include @@ -121,4 +122,9 @@ glnx_file_copy_at (int src_dfd, GCancellable *cancellable, GError **error); +gboolean +glnx_stream_fstat (GFileDescriptorBased *stream, + struct stat *stbuf, + GError **error); + G_END_DECLS From 40ef5f7400d4f8eed6a8f834917008b33ad4fb4e Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 10 May 2016 11:59:17 -0400 Subject: [PATCH 074/280] text_percent_internal: only pad right in the text case Padding in the percentage case was useless (and actually didn't work properly) since all the real estate is taken up by the text and the bar. We only need padding in the text case, in case the new string is shorter. --- glnx-console.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index bbcfe734..d40702ed 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -221,20 +221,22 @@ text_percent_internal (const char *text, if (percentage == -1) { + const guint spacelen = ncolumns - input_textlen; fwrite (text, 1, input_textlen, stdout); + printpad (spaces, n_spaces, spacelen); } else { const guint textlen = MIN (input_textlen, ncolumns - bar_min); const guint barlen = ncolumns - (textlen + 1);; - + if (textlen > 0) { fwrite (text, 1, textlen, stdout); fputc (' ', stdout); } - - { + + { const guint nbraces = 2; const guint textpercent_len = 5; const guint bar_internal_len = barlen - nbraces - textpercent_len; @@ -247,10 +249,6 @@ text_percent_internal (const char *text, fputc (']', stdout); fprintf (stdout, " %3d%%", percentage); } - - { const guint spacelen = ncolumns - textlen - barlen; - printpad (spaces, n_spaces, spacelen); - } } fflush (stdout); From 4919f6ee68c0e64465d83a8832261c477a5d673c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 28 May 2016 09:14:04 -0400 Subject: [PATCH 075/280] Introduce glnx_gen_temp_name() We have multiple copies growing again of this code. glibc has this API internally and uses it in multiple places, let's do the same. Closes: #14 --- glnx-dirfd.c | 58 +++++++++++++++++++++++++++++++++------------------- glnx-dirfd.h | 2 ++ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 4861ccfe..4a071cc7 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -277,20 +277,17 @@ glnx_fdrel_abspath (int dfd, } /** - * glnx_mkdtempat: - * @dfd: Directory fd - * @tmpl: (type filename): template directory name - * @mode: permissions to create the temporary directory with - * @error: Error + * glnx_gen_temp_name: + * @tmpl: (type filename): template directory name, the last 6 characters will be replaced * - * Similar to g_mkdtemp_full, but using openat. + * Replace the last 6 characters of @tmpl with random ASCII. You must + * use this in combination with a mechanism to ensure race-free file + * creation such as `O_EXCL`. */ -gboolean -glnx_mkdtempat (int dfd, - gchar *tmpl, - int mode, - GError **error) +void +glnx_gen_temp_name (gchar *tmpl) { + size_t len; char *XXXXXX; int count; static const char letters[] = @@ -300,17 +297,11 @@ glnx_mkdtempat (int dfd, GTimeVal tv; static int counter = 0; - g_return_val_if_fail (tmpl != NULL, -1); + g_return_if_fail (tmpl != NULL); + len = strlen (tmpl); + g_return_if_fail (len < 6); - /* find the last occurrence of "XXXXXX" */ - XXXXXX = g_strrstr (tmpl, "XXXXXX"); - - if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "Invalid temporary directory template '%s'", tmpl); - return FALSE; - } + XXXXXX = tmpl + (len - 6); /* Get some more or less random data. */ g_get_current_time (&tv); @@ -332,6 +323,31 @@ glnx_mkdtempat (int dfd, XXXXXX[4] = letters[v % NLETTERS]; v /= NLETTERS; XXXXXX[5] = letters[v % NLETTERS]; + } +} + +/** + * glnx_mkdtempat: + * @dfd: Directory fd + * @tmpl: (type filename): template directory name, last 6 characters will be replaced + * @mode: permissions to create the temporary directory with + * @error: Error + * + * Similar to g_mkdtemp_full, but using openat. + */ +gboolean +glnx_mkdtempat (int dfd, + gchar *tmpl, + int mode, + GError **error) +{ + int count; + + g_return_val_if_fail (tmpl != NULL, -1); + + for (count = 0; count < 100; count++) + { + glnx_gen_temp_name (tmpl); if (mkdirat (dfd, tmpl, mode) == -1) { diff --git a/glnx-dirfd.h b/glnx-dirfd.h index c13e3dc5..3a766952 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -81,6 +81,8 @@ gboolean glnx_opendirat (int dfd, char *glnx_fdrel_abspath (int dfd, const char *path); +void glnx_gen_temp_name (gchar *tmpl); + gboolean glnx_mkdtempat (int dfd, gchar *tmpl, int mode, From afe3c3a86178c29ceaa3a5e46397ab2fa97202b3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 31 May 2016 09:29:08 -0400 Subject: [PATCH 076/280] dirfd: Fix inverted precondition in previous tmpname commit I swear I tested it... --- glnx-dirfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 4a071cc7..95d7fe17 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -299,7 +299,7 @@ glnx_gen_temp_name (gchar *tmpl) g_return_if_fail (tmpl != NULL); len = strlen (tmpl); - g_return_if_fail (len < 6); + g_return_if_fail (len >= 6); XXXXXX = tmpl + (len - 6); From a6d08657aa868a0d5c7b5dd494e16f65415a148f Mon Sep 17 00:00:00 2001 From: Yu Qi Zhang Date: Thu, 16 Jun 2016 15:39:32 +0000 Subject: [PATCH 077/280] fdio: Delete .tmp file on failure We noticed the temp files being left over in ostree when (mistakenly) trying to replace the contents of a subpath that wasn't a directory. In the future we should look at the systemd code using `O_TMPFILE` here. --- glnx-fdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 62371d02..cdbb69fe 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -746,6 +746,8 @@ glnx_file_replace_contents_with_perms_at (int dfd, ret = TRUE; out: + if (!ret) + (void) unlink (tmppath); return ret; } From 4f83b70f690f437b983333a7c43def3ed6ca2d46 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 28 Jun 2016 11:23:47 +0200 Subject: [PATCH 078/280] glnx_release_lock_file - Don't close fd -1 (i.e. if we never locked) This happens a lot if you use autocleanup for lock files, and the function returns early without the lock being taken. --- glnx-lockfile.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-lockfile.c b/glnx-lockfile.c index be9ad3a0..7dd69e91 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -187,7 +187,8 @@ void glnx_release_lock_file(GLnxLockFile *f) { f->path = NULL; } - (void) close (f->fd); + if (f->fd != -1) + (void) close (f->fd); f->fd = -1; f->operation = 0; } From 113c770dc1e7f29d9c5478661fdd89d0035079a7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 26 Jun 2016 17:11:38 -0400 Subject: [PATCH 079/280] fdio: Add open_tmpfile_linkable() and link_tmpfile_at() We had a bug previously where we failed to clean up a temporary file in an error path. This is a classic case where the new `O_TMPFILE` API in Linux is nicer. To implement this, as usual we start with some original bits from systemd. But in this case I ended up having to heavily modify it because systemd doesn't support "link into place and overwrite". They don't actually use their tempfile code much at all in fact - as far as I can tell, just in the coredump code. Whereas in many apps, ostree included, a very common use case is atomically updating an existing file, which is `glnx_file_replace_contents_at()`, including subtleties like doing an `fdatasync()` if the file already existed. Implementing this then is slightly weird since we need to link() the file into place, then rename() after. It's still better though because if we e.g. hit `ENOSPC` halfway through, we'll clean up the file automatically. We still do keep the mode where we error out if the file exists. Finally, the ostree core though does have a more unusual case where we want to ignore EEXIST (allow concurrent object writers), so add support for that now. Note: One really confusing bug I had here was that `O_TMPFILE` ignores the provided mode, and this caused ostree to write refs that weren't world readable. Rework things so we always call `fchmod()`, but as a consequence we're no longer honoring umask in the default case. I doubt anyone will care, and if they do we should probably fix ostree to consistently use a mode inherited from the repo or something. --- glnx-fdio.c | 293 ++++++++++++++++++++++++++++++++++++----- glnx-fdio.h | 23 ++++ glnx-missing-syscall.h | 54 ++++++++ glnx-missing.h | 52 ++++++++ libglnx.m4 | 15 +++ 5 files changed, 406 insertions(+), 31 deletions(-) create mode 100644 glnx-missing-syscall.h create mode 100644 glnx-missing.h create mode 100644 libglnx.m4 diff --git a/glnx-fdio.c b/glnx-fdio.c index cdbb69fe..c15639d5 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -37,10 +37,247 @@ #include #include +#include #include #include #include #include +#include + +/* Returns the number of chars needed to format variables of the + * specified type as a decimal string. Adds in extra space for a + * negative '-' prefix (hence works correctly on signed + * types). Includes space for the trailing NUL. */ +#define DECIMAL_STR_MAX(type) \ + (2+(sizeof(type) <= 1 ? 3 : \ + sizeof(type) <= 2 ? 5 : \ + sizeof(type) <= 4 ? 10 : \ + sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) + +static gboolean +rename_file_noreplace_at (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, + gboolean ignore_eexist, + GError **error) +{ + if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0) + { + if (errno == EINVAL || errno == ENOSYS) + { + /* Fall through */ + ; + } + else if (errno == EEXIST && ignore_eexist) + { + (void) unlinkat (olddirfd, oldpath, 0); + return TRUE; + } + else + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + else + return TRUE; + + if (linkat (olddirfd, oldpath, newdirfd, newpath, 0) < 0) + { + if (errno == EEXIST && ignore_eexist) + /* Fall through */ + ; + else + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + + if (unlinkat (olddirfd, oldpath, 0) < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + return TRUE; +} + +gboolean +glnx_open_tmpfile_linkable_at (int dfd, + const char *subpath, + int flags, + int *out_fd, + char **out_path, + GError **error) +{ + glnx_fd_close int fd = -1; + int count; + + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ + g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE); + + /* Creates a temporary file, that shall be renamed to "target" + * later. If possible, this uses O_TMPFILE – in which case + * "ret_path" will be returned as NULL. If not possible a the + * tempoary path name used is returned in "ret_path". Use + * link_tmpfile() below to rename the result after writing the file + * in full. */ +#ifdef O_TMPFILE + fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); + if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP)) + { + glnx_set_prefix_error_from_errno (error, "%s", "open(O_TMPFILE)"); + return FALSE; + } + if (fd != -1) + { + *out_fd = fd; + fd = -1; + *out_path = NULL; + return TRUE; + } + /* Fallthrough */ +#endif + + { g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL); + const guint count_max = 100; + + for (count = 0; count < count_max; count++) + { + glnx_gen_temp_name (tmp); + + fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0600); + if (fd < 0) + { + if (errno == EEXIST) + continue; + else + { + glnx_set_prefix_error_from_errno (error, "%s", "Creating temp file"); + return FALSE; + } + } + else + { + *out_fd = fd; + fd = -1; + *out_path = g_steal_pointer (&tmp); + return TRUE; + } + } + } + g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "Exhausted %u attempts to create temporary file", count); + return FALSE; +} + +gboolean +glnx_link_tmpfile_at (int dfd, + GLnxLinkTmpfileReplaceMode mode, + int fd, + const char *tmpfile_path, + int target_dfd, + const char *target, + GError **error) +{ + const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE); + const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST); + + g_return_val_if_fail (fd >= 0, FALSE); + + /* Unlike the original systemd code, this function also supports + * replacing existing files. + */ + + /* We have `tmpfile_path` for old systems without O_TMPFILE. */ + if (tmpfile_path) + { + if (replace) + { + /* We have a regular tempfile, we're overwriting - this is a + * simple renameat(). + */ + if (renameat (dfd, tmpfile_path, target_dfd, target) < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + else + { + /* We need to use renameat2(..., NOREPLACE) or emulate it */ + if (!rename_file_noreplace_at (dfd, tmpfile_path, target_dfd, target, + ignore_eexist, + error)) + return FALSE; + } + } + else + { + /* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */ + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + + sprintf (proc_fd_path, "/proc/self/fd/%i", fd); + + if (replace) + { + /* In this case, we had our temp file atomically hidden, but now + * we need to make it visible in the FS so we can do a rename. + * Ideally, linkat() would gain AT_REPLACE or so. + */ + /* TODO - avoid double alloca, we can just alloca a copy of + * the pathname plus space for tmp.XXXXX */ + char *dnbuf = strdupa (target); + const char *dn = dirname (dnbuf); + char *tmpname_buf = glnx_strjoina (dn, "/tmp.XXXXXX"); + guint count; + const guint count_max = 100; + + for (count = 0; count < count_max; count++) + { + glnx_gen_temp_name (tmpname_buf); + + if (linkat (AT_FDCWD, proc_fd_path, target_dfd, tmpname_buf, AT_SYMLINK_FOLLOW) < 0) + { + if (errno == EEXIST) + continue; + else + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + else + break; + } + if (count == count_max) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, + "Exhausted %u attempts to create temporary file", count); + } + if (renameat (dfd, tmpname_buf, target_dfd, target) < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + else + { + if (linkat (AT_FDCWD, proc_fd_path, target_dfd, target, AT_SYMLINK_FOLLOW) < 0) + { + if (errno == EEXIST && mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST) + ; + else + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + } + + } + return TRUE; +} static guint8* glnx_fd_readall_malloc (int fd, @@ -658,20 +895,24 @@ glnx_file_replace_contents_with_perms_at (int dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; int r; - /* We use the /proc/self trick as there's no mkostemp_at() yet */ - g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd); + char *dnbuf = strdupa (subpath); + const char *dn = dirname (dnbuf); + g_autofree char *tmpfile_path = NULL; glnx_fd_close int fd = -1; dfd = glnx_dirfd_canonicalize (dfd); - if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC, - mode == (mode_t) -1 ? 0666 : mode)) == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + /* With O_TMPFILE we can't use umask, and we can't sanely query the + * umask...let's assume something relatively standard. + */ + if (mode == (mode_t) -1) + mode = 0644; + + if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC, + &fd, &tmpfile_path, + error)) + return FALSE; if (len == -1) len = strlen ((char*)buf); @@ -682,14 +923,14 @@ glnx_file_replace_contents_with_perms_at (int dfd, { errno = r; glnx_set_error_from_errno (error); - goto out; + return FALSE; } if ((r = glnx_loop_write (fd, buf, len)) != 0) { errno = -r; glnx_set_error_from_errno (error); - goto out; + return FALSE; } if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) @@ -702,7 +943,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (errno != ENOENT) { glnx_set_error_from_errno (error); - goto out; + return FALSE; } do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; } @@ -714,7 +955,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (fdatasync (fd) != 0) { glnx_set_error_from_errno (error); - goto out; + return FALSE; } } } @@ -724,31 +965,21 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (fchown (fd, uid, gid) != 0) { glnx_set_error_from_errno (error); - goto out; - } - } - - /* If a mode was forced, override umask */ - if (mode != (mode_t) -1) - { - if (fchmod (fd, mode) != 0) - { - glnx_set_error_from_errno (error); - goto out; + return FALSE; } } - if (renameat (dfd, tmppath, dfd, subpath) != 0) + if (fchmod (fd, mode) != 0) { glnx_set_error_from_errno (error); - goto out; + return FALSE; } - ret = TRUE; - out: - if (!ret) - (void) unlink (tmppath); - return ret; + if (!glnx_link_tmpfile_at (dfd, GLNX_LINK_TMPFILE_REPLACE, + fd, tmpfile_path, dfd, subpath, error)) + return FALSE; + + return TRUE; } /** diff --git a/glnx-fdio.h b/glnx-fdio.h index 3ca1a660..982545ab 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -46,6 +46,29 @@ const char *glnx_basename (const char *path) return (basename) (path); } +gboolean +glnx_open_tmpfile_linkable_at (int dfd, + const char *subpath, + int flags, + int *out_fd, + char **out_path, + GError **error); + +typedef enum { + GLNX_LINK_TMPFILE_REPLACE, + GLNX_LINK_TMPFILE_NOREPLACE, + GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST +} GLnxLinkTmpfileReplaceMode; + +gboolean +glnx_link_tmpfile_at (int dfd, + GLnxLinkTmpfileReplaceMode flags, + int fd, + const char *tmpfile_path, + int target_dfd, + const char *target, + GError **error); + GBytes * glnx_fd_readall_bytes (int fd, GCancellable *cancellable, diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h new file mode 100644 index 00000000..2c583c6d --- /dev/null +++ b/glnx-missing-syscall.h @@ -0,0 +1,54 @@ +/*** + This file was originally part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +/* Missing glibc definitions to access certain kernel APIs */ + +#if !HAVE_DECL_RENAMEAT2 +# ifndef __NR_renameat2 +# if defined __x86_64__ +# define __NR_renameat2 316 +# elif defined __arm__ +# define __NR_renameat2 382 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_renameat2 4351 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_renameat2 6315 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_renameat2 5311 +# endif +# elif defined __i386__ +# define __NR_renameat2 353 +# else +# warning "__NR_renameat2 unknown for your architecture" +# endif +# endif + +static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { +# ifdef __NR_renameat2 + return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif diff --git a/glnx-missing.h b/glnx-missing.h new file mode 100644 index 00000000..fa80d3e8 --- /dev/null +++ b/glnx-missing.h @@ -0,0 +1,52 @@ +#pragma once + +/*** + This file was originally part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see . +***/ + +/* Missing glibc definitions to access certain kernel APIs */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__x86_64__) + +/* The precise definition of __O_TMPFILE is arch specific, so let's + * just define this on x86 where we know the value. */ + +#ifndef __O_TMPFILE +#define __O_TMPFILE 020000000 +#endif + +/* a horrid kludge trying to make sure that this will fail on old kernels */ +#ifndef O_TMPFILE +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#endif + +#endif + +#ifndef RENAME_NOREPLACE +#define RENAME_NOREPLACE (1 << 0) +#endif + +#include "glnx-missing-syscall.h" diff --git a/libglnx.m4 b/libglnx.m4 new file mode 100644 index 00000000..6603c092 --- /dev/null +++ b/libglnx.m4 @@ -0,0 +1,15 @@ +AC_DEFUN([LIBGLNX_CONFIGURE], +[ +AC_CHECK_DECLS([ + renameat2, + ], + [], [], [[ +#include +#include +#include +#include +#include +#include +#include +]]) +]) From 78ae787757ab697bf92f49ec41a03c540550c887 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 8 Jul 2016 12:57:12 -0400 Subject: [PATCH 080/280] fdio: Use correct dfd with O_TMPFILE in rename case While auditing this code to figure out why ostree's `tests/test-refs.sh` was failing, while the bug turned out to be different, I noticed that in the case where `dfd != target_dfd`, we failed to do the right `renameat()`. (No code I'm aware of does this now). --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index c15639d5..55eef098 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -255,7 +255,7 @@ glnx_link_tmpfile_at (int dfd, g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, "Exhausted %u attempts to create temporary file", count); } - if (renameat (dfd, tmpname_buf, target_dfd, target) < 0) + if (renameat (target_dfd, tmpname_buf, target_dfd, target) < 0) { glnx_set_error_from_errno (error); return FALSE; From d2e588d94fabacfeb5b08790365161a015a00b98 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 8 Jul 2016 13:06:47 -0400 Subject: [PATCH 081/280] fdio: Add unlinkat() in error paths for tmpfiles This is kind of an ABI change but it's for the better I think; on error we consistently clean up the temp file. This is obviously necessary without `O_TMPFILE`. With it, we still need an error cleanup in the case where we're trying to replace an existing file. I noticed this in ostree's `tests/test-refs.sh` which intentionally tries to rename a file over a directory path. --- glnx-fdio.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 55eef098..f4648ed8 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -199,6 +199,7 @@ glnx_link_tmpfile_at (int dfd, */ if (renameat (dfd, tmpfile_path, target_dfd, target) < 0) { + (void) unlinkat (dfd, tmpfile_path, 0); glnx_set_error_from_errno (error); return FALSE; } @@ -209,7 +210,10 @@ glnx_link_tmpfile_at (int dfd, if (!rename_file_noreplace_at (dfd, tmpfile_path, target_dfd, target, ignore_eexist, error)) - return FALSE; + { + (void) unlinkat (dfd, tmpfile_path, 0); + return FALSE; + } } } else @@ -257,6 +261,10 @@ glnx_link_tmpfile_at (int dfd, } if (renameat (target_dfd, tmpname_buf, target_dfd, target) < 0) { + /* This is currently the only case where we need to have + * a cleanup unlinkat() still with O_TMPFILE. + */ + (void) unlinkat (target_dfd, tmpname_buf, 0); glnx_set_error_from_errno (error); return FALSE; } From c072ef1ebab052bdecb0d9a52c14813728f83859 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 22 Jul 2016 09:21:09 -0400 Subject: [PATCH 082/280] text_percent_internal: compare uints before printing A wild sordid tale of substractions and unsigned integers leads this team of variables down a loonng path... Reported-by: Gatis Paeglis --- glnx-console.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index d40702ed..416d2768 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -221,9 +221,11 @@ text_percent_internal (const char *text, if (percentage == -1) { - const guint spacelen = ncolumns - input_textlen; fwrite (text, 1, input_textlen, stdout); - printpad (spaces, n_spaces, spacelen); + + /* Overwrite remaining space, if any */ + if (ncolumns > input_textlen) + printpad (spaces, n_spaces, ncolumns - input_textlen); } else { From 80e5af921878adfd99ec2e6cb974d88b3f6f5041 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 29 Jul 2016 13:08:17 -0400 Subject: [PATCH 083/280] shutil: Use new API to iterate ensuring d_type This drops a lot of duplicate code. --- glnx-shutil.c | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/glnx-shutil.c b/glnx-shutil.c index 281e5bf0..6ff9fa68 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -26,26 +26,6 @@ #include #include -static unsigned char -struct_stat_to_dt (struct stat *stbuf) -{ - if (S_ISDIR (stbuf->st_mode)) - return DT_DIR; - if (S_ISREG (stbuf->st_mode)) - return DT_REG; - if (S_ISCHR (stbuf->st_mode)) - return DT_CHR; - if (S_ISBLK (stbuf->st_mode)) - return DT_BLK; - if (S_ISFIFO (stbuf->st_mode)) - return DT_FIFO; - if (S_ISLNK (stbuf->st_mode)) - return DT_LNK; - if (S_ISSOCK (stbuf->st_mode)) - return DT_SOCK; - return DT_UNKNOWN; -} - static gboolean glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, @@ -56,31 +36,12 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, while (TRUE) { - if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, cancellable, error)) + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (dfd_iter, &dent, cancellable, error)) goto out; if (dent == NULL) break; - if (dent->d_type == DT_UNKNOWN) - { - struct stat stbuf; - if (fstatat (dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) - { - if (errno == ENOENT) - continue; - else - { - glnx_set_error_from_errno (error); - goto out; - } - } - dent->d_type = struct_stat_to_dt (&stbuf); - /* Assume unknown types are just treated like regular files */ - if (dent->d_type == DT_UNKNOWN) - dent->d_type = DT_REG; - } - if (dent->d_type == DT_DIR) { g_auto(GLnxDirFdIterator) child_dfd_iter = { 0, }; From c2ba4d879956436c1349acb0aeafd6f885276c67 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 3 Aug 2016 11:07:59 -0400 Subject: [PATCH 084/280] Add --disable-otmpfile Some systems have bugs with it, so let's allow downstreams to easily disable it. https://bugzilla.gnome.org/show_bug.cgi?id=769453 https://github.com/ostreedev/ostree/issues/421 --- glnx-fdio.c | 2 +- libglnx.m4 | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index f4648ed8..82006604 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -122,7 +122,7 @@ glnx_open_tmpfile_linkable_at (int dfd, * tempoary path name used is returned in "ret_path". Use * link_tmpfile() below to rename the result after writing the file * in full. */ -#ifdef O_TMPFILE +#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP)) { diff --git a/libglnx.m4 b/libglnx.m4 index 6603c092..43dff975 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -12,4 +12,13 @@ AC_CHECK_DECLS([ #include #include ]]) + +AC_ARG_ENABLE(otmpfile, + [AS_HELP_STRING([--disable-otmpfile], + [Disable use of O_TMPFILE [default=no]])],, + [enable_otmpfile=yes]) +AS_IF([test $enable_otmpfile = yes], [], [ + AC_DEFINE([DISABLE_OTMPFILE], 1, [Define if we should avoid using O_TMPFILE])]) + ]) + From 5ac0d702d70b00887f9329e47f4d5653e994531a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 3 Aug 2016 11:00:08 -0400 Subject: [PATCH 085/280] fdio: Only invoke fallocate() for sizes > 0 In some cases we want to replace with zero size, and `posix_fallocate()` is documented to return `EINVAL` in this case. Making this change since I noticed it elsewhere. --- glnx-fdio.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 82006604..19144da7 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -926,12 +926,15 @@ glnx_file_replace_contents_with_perms_at (int dfd, len = strlen ((char*)buf); /* Note that posix_fallocate does *not* set errno but returns it. */ - r = posix_fallocate (fd, 0, len); - if (r != 0) + if (len > 0) { - errno = r; - glnx_set_error_from_errno (error); - return FALSE; + r = posix_fallocate (fd, 0, len); + if (r != 0) + { + errno = r; + glnx_set_error_from_errno (error); + return FALSE; + } } if ((r = glnx_loop_write (fd, buf, len)) != 0) From 871617d51984604e28483d959e37fd6ce4524b0e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 5 Aug 2016 14:38:18 +0100 Subject: [PATCH 086/280] Add missing files to libglnx distribution Signed-off-by: Simon McVittie --- Makefile-libglnx.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 2e8da015..e094ef67 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -37,6 +37,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-lockfile.c \ $(libglnx_srcpath)/glnx-libcontainer.h \ $(libglnx_srcpath)/glnx-libcontainer.c \ + $(libglnx_srcpath)/glnx-missing-syscall.h \ + $(libglnx_srcpath)/glnx-missing.h \ $(libglnx_srcpath)/glnx-xattrs.h \ $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ From 4ae5e3beaaa674abfabf7404ab6fafcc4ec547db Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 7 Aug 2016 07:21:55 -0400 Subject: [PATCH 087/280] libcontainer: Add a fd-relative API I'm porting rpm-ostree and need this. Of course all this libcontainer stuff will be nuked in favor of bubblewrap when everything comes together. --- glnx-libcontainer.c | 38 +++++++++++++++++++++++++++----------- glnx-libcontainer.h | 4 ++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c index 38c1937f..6b9a2d64 100644 --- a/glnx-libcontainer.c +++ b/glnx-libcontainer.c @@ -33,6 +33,7 @@ #include #include "glnx-libcontainer.h" +#include "glnx-dirfd.h" #include "glnx-backport-autocleanups.h" #include "glnx-local-alloc.h" @@ -104,9 +105,9 @@ glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) } #endif -/* Based on code from nspawn.c */ +/* Based on code from nspawn.c; assumes process cwd is the target */ static int -glnx_libcontainer_make_api_mounts (const char *dest) +glnx_libcontainer_make_api_mounts (void) { typedef struct MountPoint { const char *what; @@ -134,10 +135,11 @@ glnx_libcontainer_make_api_mounts (const char *dest) for (k = 0; k < G_N_ELEMENTS(mount_table); k++) { - g_autofree char *where = NULL; + const char *where = mount_table[k].where; int t; - where = g_build_filename (dest, mount_table[k].where, NULL); + g_assert (where[0] == '/'); + where++; t = mkdir (where, 0755); if (t < 0 && errno != EEXIST) @@ -201,9 +203,9 @@ glnx_libcontainer_prep_dev (const char *dest_devdir) } pid_t -glnx_libcontainer_run_chroot_private (const char *dest, - const char *binary, - char **argv) +glnx_libcontainer_run_chroot_at_private (int dfd, + const char *binary, + char **argv) { /* Make most new namespaces; note our use of CLONE_NEWNET means we * have no networking in the container root. @@ -250,12 +252,12 @@ glnx_libcontainer_run_chroot_private (const char *dest, } } - if (chdir (dest) != 0) - _perror_fatal ("chdir: "); + if (fchdir (dfd) != 0) + _perror_fatal ("fchdir: "); if (!in_container) { - if (glnx_libcontainer_make_api_mounts (dest) != 0) + if (glnx_libcontainer_make_api_mounts () != 0) _perror_fatal ("preparing api mounts: "); if (glnx_libcontainer_prep_dev ("dev") != 0) @@ -264,7 +266,7 @@ glnx_libcontainer_run_chroot_private (const char *dest, if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0) _perror_fatal ("mount (MS_BIND)"); - if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0) + if (mount (".", "/", NULL, MS_MOVE, NULL) != 0) _perror_fatal ("mount (MS_MOVE)"); } @@ -295,3 +297,17 @@ glnx_libcontainer_run_chroot_private (const char *dest, g_assert_not_reached (); } + +pid_t +glnx_libcontainer_run_chroot_private (const char *dest, + const char *binary, + char **argv) +{ + glnx_fd_close int dfd = -1; + + dfd = glnx_opendirat_with_errno (AT_FDCWD, dest, TRUE); + if (dfd < 0) + return -1; + + return glnx_libcontainer_run_chroot_at_private (dfd, binary, argv); +} diff --git a/glnx-libcontainer.h b/glnx-libcontainer.h index 10855db7..9ac83d40 100644 --- a/glnx-libcontainer.h +++ b/glnx-libcontainer.h @@ -31,6 +31,10 @@ #include #include +pid_t glnx_libcontainer_run_chroot_at_private (int root_dfd, + const char *binary, + char **argv); + pid_t glnx_libcontainer_run_chroot_private (const char *dest, const char *binary, char **argv); From 7ce80827bdb7f0388151dfb2d67209b1977ab5a7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 30 Aug 2016 14:34:21 -0400 Subject: [PATCH 088/280] Remove libcontainer No longer used by anything; see https://github.com/projectatomic/rpm-ostree/pull/429 --- Makefile-libglnx.am | 2 - README.md | 1 - glnx-libcontainer.c | 313 -------------------------------------------- glnx-libcontainer.h | 40 ------ libglnx.h | 1 - 5 files changed, 357 deletions(-) delete mode 100644 glnx-libcontainer.c delete mode 100644 glnx-libcontainer.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index e094ef67..a812755e 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -35,8 +35,6 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-fdio.c \ $(libglnx_srcpath)/glnx-lockfile.h \ $(libglnx_srcpath)/glnx-lockfile.c \ - $(libglnx_srcpath)/glnx-libcontainer.h \ - $(libglnx_srcpath)/glnx-libcontainer.c \ $(libglnx_srcpath)/glnx-missing-syscall.h \ $(libglnx_srcpath)/glnx-missing.h \ $(libglnx_srcpath)/glnx-xattrs.h \ diff --git a/README.md b/README.md index 50a5f90d..241f5cef 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Features: - APIs also have a `GError` parameter - High level "shutil", somewhat inspired by Python's - A "console" API for tty output - - Some basic container utility functions - A backport of the GLib cleanup macros for projects which can't yet take a dependency on 2.40. diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c deleted file mode 100644 index 6b9a2d64..00000000 --- a/glnx-libcontainer.c +++ /dev/null @@ -1,313 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Portions derived from src/nspawn/nspawn.c: - * Copyright 2010 Lennart Poettering - * - * Copyright (C) 2014,2015 Colin Walters - * - * This program 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 of the licence 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., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glnx-libcontainer.h" -#include "glnx-dirfd.h" - -#include "glnx-backport-autocleanups.h" -#include "glnx-local-alloc.h" - -static void _perror_fatal (const char *message) __attribute__ ((noreturn)); - -static void -_perror_fatal (const char *message) -{ - perror (message); - exit (1); -} - -typedef enum { - CONTAINER_UNINIT = 0, - CONTAINER_YES = 1, - CONTAINER_NO = 2 -} ContainerDetectionState; - -static gboolean -currently_in_container (void) -{ - static gsize container_detected = CONTAINER_UNINIT; - - if (g_once_init_enter (&container_detected)) - { - ContainerDetectionState tmp_state = CONTAINER_NO; - struct stat stbuf; - - /* http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/ */ - if (getenv ("container") != NULL - || stat ("/.dockerinit", &stbuf) == 0) - tmp_state = CONTAINER_YES; - /* But since Docker isn't on board, yet, so... - http://stackoverflow.com/questions/23513045/how-to-check-if-a-process-is-running-inside-docker-container */ - g_once_init_leave (&container_detected, tmp_state); - } - return container_detected == CONTAINER_YES; -} - -#if 0 -static gboolean -glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) -{ - gboolean ret = FALSE; - - if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0) - { - int errsv = errno; - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "mount(%s, MS_BIND): %s", - path, - g_strerror (errsv)); - goto out; - } - if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0) - { - int errsv = errno; - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "mount(%s, MS_BIND | MS_RDONLY): %s", - path, - g_strerror (errsv)); - goto out; - } - - ret = TRUE; - out: - return ret; -} -#endif - -/* Based on code from nspawn.c; assumes process cwd is the target */ -static int -glnx_libcontainer_make_api_mounts (void) -{ - typedef struct MountPoint { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; - gboolean fatal; - } MountPoint; - - static const MountPoint mount_table[] = { - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, - { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, TRUE }, /* Bind mount first */ - { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, TRUE }, /* Then, make it r/o */ - { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, - { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, TRUE }, - { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, - { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, FALSE }, /* Bind mount first */ - { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, FALSE }, /* Then, make it r/o */ - }; - - unsigned k; - - for (k = 0; k < G_N_ELEMENTS(mount_table); k++) - { - const char *where = mount_table[k].where; - int t; - - g_assert (where[0] == '/'); - where++; - - t = mkdir (where, 0755); - if (t < 0 && errno != EEXIST) - { - if (!mount_table[k].fatal) - continue; - return -1; - } - - if (mount (mount_table[k].what, - where, - mount_table[k].type, - mount_table[k].flags, - mount_table[k].options) < 0) - { - if (errno == ENOENT && !mount_table[k].fatal) - continue; - return -1; - } - } - - return 0; -} - -static int -glnx_libcontainer_prep_dev (const char *dest_devdir) -{ - glnx_fd_close int src_fd = -1; - glnx_fd_close int dest_fd = -1; - struct stat stbuf; - guint i; - static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; - - src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (src_fd == -1) - return -1; - - dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); - if (dest_fd == -1) - return -1; - - for (i = 0; i < G_N_ELEMENTS (devnodes); i++) - { - const char *nodename = devnodes[i]; - - if (fstatat (src_fd, nodename, &stbuf, 0) == -1) - { - if (errno == ENOENT) - continue; - else - return -1; - } - - if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0) - return -1; - if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0) - return -1; - } - - return 0; -} - -pid_t -glnx_libcontainer_run_chroot_at_private (int dfd, - const char *binary, - char **argv) -{ - /* Make most new namespaces; note our use of CLONE_NEWNET means we - * have no networking in the container root. - */ - const int cloneflags = - SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS; - pid_t child; - gboolean in_container = currently_in_container (); - - if (!in_container) - { - if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0) - return -1; - } - else - { - if ((child = fork ()) < 0) - return -1; - } - - if (child != 0) - return child; - - if (!in_container) - { - if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) - { - if (errno == EINVAL) - { - /* Ok, we may be inside a mock chroot or the like. In - * that case, let's just fall back to not - * containerizing. - */ - in_container = TRUE; - } - else - _perror_fatal ("mount: "); - } - - if (!in_container) - { - if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) - _perror_fatal ("mount (MS_NOSUID): "); - } - } - - if (fchdir (dfd) != 0) - _perror_fatal ("fchdir: "); - - if (!in_container) - { - if (glnx_libcontainer_make_api_mounts () != 0) - _perror_fatal ("preparing api mounts: "); - - if (glnx_libcontainer_prep_dev ("dev") != 0) - _perror_fatal ("preparing /dev: "); - - if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0) - _perror_fatal ("mount (MS_BIND)"); - - if (mount (".", "/", NULL, MS_MOVE, NULL) != 0) - _perror_fatal ("mount (MS_MOVE)"); - } - - if (chroot (".") != 0) - _perror_fatal ("chroot: "); - - if (chdir ("/") != 0) - _perror_fatal ("chdir: "); - - /* Environment variables like PATH in the end are distribution - * specific. The most correct thing would be to run through PAM, - * but that's a huge level of pain. We'd like to drive towards a - * standard /usr/bin (i.e. unified sbin too), but for now this is - * pretty compatible. - */ - setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); - - if (binary[0] == '/') - { - if (execv (binary, argv) != 0) - _perror_fatal ("execv: "); - } - else - { - if (execvp (binary, argv) != 0) - _perror_fatal ("execvp: "); - } - - g_assert_not_reached (); -} - -pid_t -glnx_libcontainer_run_chroot_private (const char *dest, - const char *binary, - char **argv) -{ - glnx_fd_close int dfd = -1; - - dfd = glnx_opendirat_with_errno (AT_FDCWD, dest, TRUE); - if (dfd < 0) - return -1; - - return glnx_libcontainer_run_chroot_at_private (dfd, binary, argv); -} diff --git a/glnx-libcontainer.h b/glnx-libcontainer.h deleted file mode 100644 index 9ac83d40..00000000 --- a/glnx-libcontainer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2014,2015 Colin Walters - * - * This program 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 of the licence 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., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -pid_t glnx_libcontainer_run_chroot_at_private (int root_dfd, - const char *binary, - char **argv); - -pid_t glnx_libcontainer_run_chroot_private (const char *dest, - const char *binary, - char **argv); diff --git a/libglnx.h b/libglnx.h index 451740d2..a5b23d07 100644 --- a/libglnx.h +++ b/libglnx.h @@ -33,7 +33,6 @@ G_BEGIN_DECLS #include #include #include -#include #include #include From 1e7b96808a168f8ea9066194613716fb0b27d559 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Wed, 31 Aug 2016 13:16:06 -0700 Subject: [PATCH 089/280] Distribute libglnx.m4 This is needed by ostree when creating a tarball with make dist. --- Makefile-libglnx.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index a812755e..64d6a9ad 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -15,7 +15,11 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING +EXTRA_DIST += \ + $(libglnx_srcpath)/README.md \ + $(libglnx_srcpath)/COPYING \ + $(libglnx_srcpath)/libglnx.m4 \ + $(NULL) libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-alloca.h \ From 36396b49ad6636c9959f3dfac5e04d41584b1a92 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 5 Oct 2016 09:54:41 -0400 Subject: [PATCH 090/280] build: Add --enable-wrpseudo-compat See https://mail.gnome.org/archives/ostree-list/2016-October/msg00003.html Basically https://github.com/wrpseudo/pseudo doesn't implement newer APIs like renameat2() and O_TMPFILE, so on the host side (as potentially opposed to the target system) we want to be able to disable them. --- glnx-fdio.c | 4 +++- libglnx.m4 | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 19144da7..7ee57cd6 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -60,6 +60,7 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, gboolean ignore_eexist, GError **error) { +#ifndef ENABLE_WRPSEUDO_COMPAT if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0) { if (errno == EINVAL || errno == ENOSYS) @@ -80,6 +81,7 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, } else return TRUE; +#endif if (linkat (olddirfd, oldpath, newdirfd, newpath, 0) < 0) { @@ -122,7 +124,7 @@ glnx_open_tmpfile_linkable_at (int dfd, * tempoary path name used is returned in "ret_path". Use * link_tmpfile() below to rename the result after writing the file * in full. */ -#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) +#if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT) fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP)) { diff --git a/libglnx.m4 b/libglnx.m4 index 43dff975..9b2e30c9 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -20,5 +20,12 @@ AC_ARG_ENABLE(otmpfile, AS_IF([test $enable_otmpfile = yes], [], [ AC_DEFINE([DISABLE_OTMPFILE], 1, [Define if we should avoid using O_TMPFILE])]) -]) +AC_ARG_ENABLE(wrpseudo-compat, + [AS_HELP_STRING([--enable-wrpseudo-compat], + [Disable use syscall() and filesystem calls to for compatibility with wrpseudo [default=no]])],, + [enable_wrpseudo_compat=no]) +AS_IF([test $enable_wrpseudo_compat = no], [], [ + AC_DEFINE([ENABLE_WRPSEUDO_COMPAT], 1, [Define if we should be compatible with wrpseudo])]) +dnl end LIBGLNX_CONFIGURE +]) From 7d2f577d76eeb83a6f7a914a7acd3d277d4cbe7c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 25 Oct 2016 12:20:45 -0400 Subject: [PATCH 091/280] fdio: Make GLnxFileCopyFlags actually flags I wanted to add a new one, and realized it was wrong. Luckily, I think we were safe until now, since the set of bits for `(0, 1, 2)` is actually distinct. Although, hm, callers specifying `GLNX_FILE_COPY_OVERWRITE` may have not actually been getting that. --- glnx-fdio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 982545ab..111df9d5 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -130,9 +130,9 @@ int glnx_loop_write (int fd, const void *buf, size_t nbytes); typedef enum { - GLNX_FILE_COPY_OVERWRITE, - GLNX_FILE_COPY_NOXATTRS, - GLNX_FILE_COPY_DATASYNC + GLNX_FILE_COPY_OVERWRITE = (1 << 0), + GLNX_FILE_COPY_NOXATTRS = (1 << 1), + GLNX_FILE_COPY_DATASYNC = (1 << 2) } GLnxFileCopyFlags; gboolean From abd37a4790f86f53bfb442e6d80e1710f50bff92 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 24 Oct 2016 16:16:06 -0400 Subject: [PATCH 092/280] dirfd: Set initialized flag for iters And use it when deinitializing, to avoid calling `closedir(NULL)`. In practice, this doesn't matter, because `closedir` *does* handle `NULL` in glibc. However, I'm playing with the GCC `-fsanitize=undefined`, and it aborts because `closedir` is tagged as requiring a non-`NULL` pointer. --- glnx-dirfd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 95d7fe17..3a02bb0a 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -142,6 +142,7 @@ glnx_dirfd_iterator_init_take_fd (int dfd, real_dfd_iter->fd = dfd; real_dfd_iter->d = d; + real_dfd_iter->initialized = TRUE; ret = TRUE; out: @@ -169,6 +170,7 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; g_return_val_if_fail (out_dent, FALSE); + g_return_val_if_fail (dfd_iter->initialized, FALSE); if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; @@ -250,6 +252,8 @@ glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter) { GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; /* fd is owned by dfd_iter */ + if (!real_dfd_iter->initialized) + return; (void) closedir (real_dfd_iter->d); real_dfd_iter->initialized = FALSE; } From 597f03b4058a4d495252ee5479371c0b428fe40f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 26 Jan 2017 08:49:03 -0500 Subject: [PATCH 093/280] dirfd: Use better and faster random algorithm for gen_temp_name() I was looking at ostree performance, and a surprising amount of time was spent in `glnx_gen_temp_name()`. We end up calling it from the main loop, and the iteration here shows up in my perf profiles. The glibc algorithm here that we adopted is *very* dated; let's switch to use `GRand`, which gives us a better algorithm. It'd be even better of course to use `getrandom()`, but we should do that in glib at some point. While I had the patient open, I extended the charset with lowercase, to better avoid collisions. --- glnx-dirfd.c | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 3a02bb0a..11388c18 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -293,13 +293,10 @@ glnx_gen_temp_name (gchar *tmpl) { size_t len; char *XXXXXX; - int count; + int i; static const char letters[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; static const int NLETTERS = sizeof (letters) - 1; - glong value; - GTimeVal tv; - static int counter = 0; g_return_if_fail (tmpl != NULL); len = strlen (tmpl); @@ -307,27 +304,8 @@ glnx_gen_temp_name (gchar *tmpl) XXXXXX = tmpl + (len - 6); - /* Get some more or less random data. */ - g_get_current_time (&tv); - value = (tv.tv_usec ^ tv.tv_sec) + counter++; - - for (count = 0; count < 100; value += 7777, ++count) - { - glong v = value; - - /* Fill in the random bits. */ - XXXXXX[0] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[1] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[2] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[3] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[4] = letters[v % NLETTERS]; - v /= NLETTERS; - XXXXXX[5] = letters[v % NLETTERS]; - } + for (i = 0; i < 6; i++) + XXXXXX[i] = letters[g_random_int_range(0, NLETTERS)]; } /** From 6bf55255e8a688997973d9d40755fa999eda3791 Mon Sep 17 00:00:00 2001 From: William Manley Date: Tue, 10 Jan 2017 15:13:56 +0000 Subject: [PATCH 094/280] listxattr: Don't assume that first call to listxattr gives correct size To get the right sized buffer to pass to `flistattr` and `llistattr` we first call them with a zero byte buffer. They then return the number of bytes they'll actually need to operate. We would `malloc` and then call again assuming that the size we got originally was correct. On my computer at least this isn't always the case. I've seen instances where the first call returns 23B, but then on the second one returns no data at all. Getting these non-existant xattrs would then cause ostree to fail. I'm not sure why it's behaving this way on my machine. I suspect its some interaction with overlayfs but I haven't proven this. --- glnx-xattrs.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index b5a59228..d50b3c23 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -135,7 +135,7 @@ get_xattrs_impl (const char *path, GError **error) { gboolean ret = FALSE; - ssize_t bytes_read; + ssize_t bytes_read, real_size; glnx_free char *xattr_names = NULL; glnx_free char *xattr_names_canonical = NULL; GVariantBuilder builder; @@ -158,15 +158,19 @@ get_xattrs_impl (const char *path, else if (bytes_read > 0) { xattr_names = g_malloc (bytes_read); - if (llistxattr (path, xattr_names, bytes_read) < 0) + real_size = llistxattr (path, xattr_names, bytes_read); + if (real_size < 0) { glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); goto out; } - xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); - - if (!read_xattr_name_array (path, -1, xattr_names_canonical, bytes_read, &builder, error)) - goto out; + else if (real_size > 0) + { + xattr_names_canonical = canonicalize_xattrs (xattr_names, real_size); + + if (!read_xattr_name_array (path, -1, xattr_names_canonical, real_size, &builder, error)) + goto out; + } } ret_xattrs = g_variant_builder_end (&builder); @@ -202,7 +206,7 @@ glnx_fd_get_all_xattrs (int fd, GError **error) { gboolean ret = FALSE; - ssize_t bytes_read; + ssize_t bytes_read, real_size; glnx_free char *xattr_names = NULL; glnx_free char *xattr_names_canonical = NULL; GVariantBuilder builder; @@ -225,15 +229,19 @@ glnx_fd_get_all_xattrs (int fd, else if (bytes_read > 0) { xattr_names = g_malloc (bytes_read); - if (flistxattr (fd, xattr_names, bytes_read) < 0) + real_size = flistxattr (fd, xattr_names, bytes_read); + if (real_size < 0) { glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); goto out; } - xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); - - if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, bytes_read, &builder, error)) - goto out; + else if (real_size > 0) + { + xattr_names_canonical = canonicalize_xattrs (xattr_names, real_size); + + if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, real_size, &builder, error)) + goto out; + } } ret_xattrs = g_variant_builder_end (&builder); From afd178fb521a7373930dc8973b115c7ff62f6ee0 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 10 Jan 2017 22:04:51 -0500 Subject: [PATCH 095/280] xattrs: Handle xattrs changing size concurrently We should be robust in the face of this and return a snapshot of the current value we saw, not transiently fail. This is the semantics we expect with ostree upgrades for `/etc` for example. --- glnx-xattrs.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index d50b3c23..e535b18c 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -83,19 +83,22 @@ read_xattr_name_array (const char *path, funcstr = fd != -1 ? "fgetxattr" : "lgetxattr"; - p = xattrs; - while (p < xattrs+len) + for (p = xattrs; p < xattrs+len; p = p + strlen (p) + 1) { ssize_t bytes_read; - char *buf; - GBytes *bytes = NULL; + g_autofree char *buf = NULL; + g_autoptr(GBytes) bytes = NULL; + again: if (fd != -1) bytes_read = fgetxattr (fd, p, NULL, 0); else bytes_read = lgetxattr (path, p, NULL, 0); if (bytes_read < 0) { + if (errno == ENODATA) + continue; + glnx_set_prefix_error_from_errno (error, "%s", funcstr); goto out; } @@ -103,26 +106,30 @@ read_xattr_name_array (const char *path, continue; buf = g_malloc (bytes_read); - bytes = g_bytes_new_take (buf, bytes_read); if (fd != -1) r = fgetxattr (fd, p, buf, bytes_read); else r = lgetxattr (path, p, buf, bytes_read); if (r < 0) { - g_bytes_unref (bytes); + if (errno == ERANGE) + { + g_free (g_steal_pointer (&buf)); + goto again; + } + else if (errno == ENODATA) + continue; + glnx_set_prefix_error_from_errno (error, "%s", funcstr); goto out; } - + + bytes = g_bytes_new_take (g_steal_pointer (&buf), bytes_read); g_variant_builder_add (builder, "(@ay@ay)", g_variant_new_bytestring (p), variant_new_ay_bytes (bytes)); - - p = p + strlen (p) + 1; - g_bytes_unref (bytes); } - + ret = TRUE; out: return ret; From 1ac35488f1dc156c5abb0c649fb50edcf7199fac Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 10 Jan 2017 22:11:16 -0500 Subject: [PATCH 096/280] xattrs: Dedup fd reading code By taking both fd and path into one copy of the reader func, exactly like we do in `read_xattr_name_array`, we can abstract over the difference. Preparatory cleanup for more work here. --- glnx-xattrs.c | 75 +++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index e535b18c..c52cd0f8 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -137,9 +137,10 @@ read_xattr_name_array (const char *path, static gboolean get_xattrs_impl (const char *path, + int fd, GVariant **out_xattrs, - GCancellable *cancellable, - GError **error) + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; ssize_t bytes_read, real_size; @@ -149,10 +150,15 @@ get_xattrs_impl (const char *path, gboolean builder_initialized = FALSE; g_autoptr(GVariant) ret_xattrs = NULL; + g_assert (path != NULL || fd != -1); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); builder_initialized = TRUE; - bytes_read = llistxattr (path, NULL, 0); + if (path) + bytes_read = llistxattr (path, NULL, 0); + else + bytes_read = flistxattr (fd, NULL, 0); if (bytes_read < 0) { @@ -165,7 +171,10 @@ get_xattrs_impl (const char *path, else if (bytes_read > 0) { xattr_names = g_malloc (bytes_read); - real_size = llistxattr (path, xattr_names, bytes_read); + if (path) + real_size = llistxattr (path, xattr_names, bytes_read); + else + real_size = flistxattr (fd, xattr_names, bytes_read); if (real_size < 0) { glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); @@ -175,7 +184,7 @@ get_xattrs_impl (const char *path, { xattr_names_canonical = canonicalize_xattrs (xattr_names, real_size); - if (!read_xattr_name_array (path, -1, xattr_names_canonical, real_size, &builder, error)) + if (!read_xattr_name_array (path, fd, xattr_names_canonical, real_size, &builder, error)) goto out; } } @@ -212,56 +221,8 @@ glnx_fd_get_all_xattrs (int fd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - ssize_t bytes_read, real_size; - glnx_free char *xattr_names = NULL; - glnx_free char *xattr_names_canonical = NULL; - GVariantBuilder builder; - gboolean builder_initialized = FALSE; - g_autoptr(GVariant) ret_xattrs = NULL; - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); - builder_initialized = TRUE; - - bytes_read = flistxattr (fd, NULL, 0); - - if (bytes_read < 0) - { - if (errno != ENOTSUP) - { - glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); - goto out; - } - } - else if (bytes_read > 0) - { - xattr_names = g_malloc (bytes_read); - real_size = flistxattr (fd, xattr_names, bytes_read); - if (real_size < 0) - { - glnx_set_prefix_error_from_errno (error, "%s", "flistxattr"); - goto out; - } - else if (real_size > 0) - { - xattr_names_canonical = canonicalize_xattrs (xattr_names, real_size); - - if (!read_xattr_name_array (NULL, fd, xattr_names_canonical, real_size, &builder, error)) - goto out; - } - } - - ret_xattrs = g_variant_builder_end (&builder); - builder_initialized = FALSE; - g_variant_ref_sink (ret_xattrs); - - ret = TRUE; - if (out_xattrs) - *out_xattrs = g_steal_pointer (&ret_xattrs); - out: - if (!builder_initialized) - g_variant_builder_clear (&builder); - return ret; + return get_xattrs_impl (NULL, fd, out_xattrs, + cancellable, error); } /** @@ -284,7 +245,7 @@ glnx_dfd_name_get_all_xattrs (int dfd, { if (dfd == AT_FDCWD || dfd == -1) { - return get_xattrs_impl (name, out_xattrs, cancellable, error); + return get_xattrs_impl (name, -1, out_xattrs, cancellable, error); } else { @@ -293,7 +254,7 @@ glnx_dfd_name_get_all_xattrs (int dfd, * https://mail.gnome.org/archives/ostree-list/2014-February/msg00017.html */ snprintf (buf, sizeof (buf), "/proc/self/fd/%d/%s", dfd, name); - return get_xattrs_impl (buf, out_xattrs, cancellable, error); + return get_xattrs_impl (buf, -1, out_xattrs, cancellable, error); } } From 7be21dee4d0823dd2fe127c7a13254f960e5386a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 10 Jan 2017 22:24:12 -0500 Subject: [PATCH 097/280] xattrs: Handle ERANGE This is symmetric with an earlier commit which handled a transition from `size != 0` -> `size = 0`. Now if xattrs are added we retry. --- glnx-xattrs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index c52cd0f8..eadb6b15 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -155,6 +155,7 @@ get_xattrs_impl (const char *path, g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); builder_initialized = TRUE; + again: if (path) bytes_read = llistxattr (path, NULL, 0); else @@ -177,6 +178,11 @@ get_xattrs_impl (const char *path, real_size = flistxattr (fd, xattr_names, bytes_read); if (real_size < 0) { + if (errno == ERANGE) + { + g_free (xattr_names); + goto again; + } glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); goto out; } From 7a703638d12410bb58ca6d85d6556d03c2a5a7ce Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 29 Jan 2017 03:14:29 -0500 Subject: [PATCH 098/280] xattrs: Add a test case for previous commits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is actually the first test case in libglnx 🙌; hopefully the consumers are prepared for us injecting into `TESTS`. --- Makefile-libglnx.am | 7 + tests/test-libglnx-xattrs.c | 282 ++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 tests/test-libglnx-xattrs.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 64d6a9ad..d3a46e55 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -51,3 +51,10 @@ libglnx_la_SOURCES = \ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) + +TESTS += test-libglnx-xattrs + +check_PROGRAMS += test-libglnx-xattrs +test_libglnx_xattrs_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-xattrs.c +test_libglnx_xattrs_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +test_libglnx_xattrs_LDADD = $(libglnx_libs) libglnx.la diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c new file mode 100644 index 00000000..9a398701 --- /dev/null +++ b/tests/test-libglnx-xattrs.c @@ -0,0 +1,282 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Red Hat, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx.h" +#include +#include +#include +#include + +#define XATTR_THREAD_RUN_TIME_USECS (5 * G_USEC_PER_SEC) + +struct XattrWorker { + int dfd; + gboolean is_writer; + guint n_attrs_read; +}; + +typedef enum { + WRITE_RUN_MUTATE, + WRITE_RUN_CREATE, +} WriteType; + +static gboolean +set_random_xattr_value (int fd, const char *name, GError **error) +{ + const guint8 randxattrbyte = g_random_int_range (0, 256); + const guint32 randxattrvalue_len = g_random_int () % 256; /* Picked to be not too small or large */ + g_autofree char *randxattrvalue = g_malloc (randxattrvalue_len); + + memset (randxattrvalue, randxattrbyte, randxattrvalue_len); + + if (fsetxattr (fd, name, randxattrvalue, randxattrvalue_len, 0) < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + return TRUE; +} + +static gboolean +add_random_xattrs (int fd, GError **error) +{ + const guint nattrs = MIN (2, g_random_int () % 16); + + for (guint i = 0; i < nattrs; i++) + { + guint32 randxattrname_v = g_random_int (); + g_autofree char *randxattrname = g_strdup_printf ("user.test%u", randxattrname_v); + + if (!set_random_xattr_value (fd, randxattrname, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +do_write_run (GLnxDirFdIterator *dfd_iter, GError **error) +{ + WriteType wtype = g_random_int () % 2; + + if (wtype == WRITE_RUN_CREATE) + { + guint32 randname_v = g_random_int (); + g_autofree char *randname = g_strdup_printf ("file%u", randname_v); + glnx_fd_close int fd = -1; + + again: + fd = openat (dfd_iter->fd, randname, O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + if (errno == EEXIST) + { + g_printerr ("Congratulations! I suggest purchasing a lottery ticket today!\n"); + goto again; + } + else + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + + if (!add_random_xattrs (fd, error)) + return FALSE; + } + else if (wtype == WRITE_RUN_MUTATE) + { + while (TRUE) + { + g_autoptr(GVariant) current_xattrs = NULL; + glnx_fd_close int fd = -1; + + struct dirent *dent; + if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error)) + return FALSE; + if (!dent) + break; + + fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error)) + return FALSE; + + for (int i = 0; i < g_variant_n_children (current_xattrs); i++) + { + const char *name, *value; + g_variant_get_child (current_xattrs, i, "(^&ay^&ay)", &name, &value); + + /* We don't want to potentially test/change xattrs like security.selinux + * that were injected by the system. + */ + if (!g_str_has_prefix (name, "user.test")) + continue; + + if (!set_random_xattr_value (fd, name, error)) + return FALSE; + } + } + } + else + g_assert_not_reached (); + + return TRUE; +} + +static gboolean +do_read_run (GLnxDirFdIterator *dfd_iter, + guint *out_n_read, + GError **error) +{ + guint nattrs = 0; + while (TRUE) + { + g_autoptr(GVariant) current_xattrs = NULL; + glnx_fd_close int fd = -1; + + struct dirent *dent; + if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error)) + return FALSE; + if (!dent) + break; + + fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + + if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error)) + return FALSE; + + /* We don't actually care about the values, just use the variable + * to avoid compiler warnings. + */ + nattrs += g_variant_n_children (current_xattrs); + } + + *out_n_read = nattrs; + return TRUE; +} + +static gpointer +xattr_thread (gpointer data) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + struct XattrWorker *worker = data; + guint64 end_time = g_get_monotonic_time () + XATTR_THREAD_RUN_TIME_USECS; + guint n_read; + + while (g_get_monotonic_time () < end_time) + { + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + + if (!glnx_dirfd_iterator_init_at (worker->dfd, ".", TRUE, &dfd_iter, error)) + goto out; + + if (worker->is_writer) + { + if (!do_write_run (&dfd_iter, error)) + goto out; + } + else + { + if (!do_read_run (&dfd_iter, &n_read, error)) + goto out; + } + } + + out: + g_assert_no_error (local_error); + + return GINT_TO_POINTER (n_read); +} + +static void +test_xattr_races (void) +{ + /* If for some reason we're built in a VM which only has one vcpu, let's still + * at least make the test do something. + */ + /* FIXME - this deadlocks for me on 4.9.4-201.fc25.x86_64, whether + * using overlayfs or xfs as source/dest. + */ + const guint nprocs = MAX (4, g_get_num_processors ()); + struct XattrWorker wdata[nprocs]; + GThread *threads[nprocs]; + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + glnx_fd_close int dfd = -1; + g_autofree char *tmpdir = g_strdup_printf ("%s/libglnx-xattrs-XXXXXX", + getenv ("TMPDIR") ?: "/var/tmp"); + guint nread = 0; + + if (!glnx_mkdtempat (AT_FDCWD, tmpdir, 0700, error)) + goto out; + + if (!glnx_opendirat (AT_FDCWD, tmpdir, TRUE, &dfd, error)) + goto out; + + for (guint i = 0; i < nprocs; i++) + { + struct XattrWorker *worker = &wdata[i]; + worker->dfd = dfd; + worker->is_writer = i % 2 == 0; + threads[i] = g_thread_new (NULL, xattr_thread, worker); + } + + for (guint i = 0; i < nprocs; i++) + { + if (wdata[i].is_writer) + (void) g_thread_join (threads[i]); + else + nread += GPOINTER_TO_UINT (g_thread_join (threads[i])); + } + + g_print ("Read %u xattrs race free!\n", nread); + + (void) glnx_shutil_rm_rf_at (AT_FDCWD, tmpdir, NULL, NULL); + + out: + g_assert_no_error (local_error); +} + +int main (int argc, char **argv) +{ + int ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/xattr-races", test_xattr_races); + + ret = g_test_run(); + + return ret; +} From 2a71cb6c5bebd0874f16c18260616dec318a823a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 11 Feb 2017 08:59:54 -0500 Subject: [PATCH 099/280] COPYING: Bump to LGPL 2.1 due to systemd import We originally inherited LGPL 2.0 from glib I think. But I didn't notice when importing systemd code it's LGPL 2.1. While individual file licenses still apply; I'm not going to bother bumping all of them to 2.1, the complete module should be viewed as under 2.1. Bump the master COPYING file accordingly. --- COPYING | 203 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 112 insertions(+), 91 deletions(-) diff --git a/COPYING b/COPYING index 5bc8fb2c..4362b491 100644 --- a/COPYING +++ b/COPYING @@ -1,13 +1,14 @@ - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 - Copyright (C) 1991 Free Software Foundation, Inc. + 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 library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] +[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 @@ -16,97 +17,109 @@ 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 Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, 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. + 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 -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 library, or if you modify it. +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 a program 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 +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. - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal + 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. - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. + 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, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. + 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, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. - GNU LIBRARY GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". + 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 @@ -133,7 +146,7 @@ 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 @@ -255,7 +268,7 @@ 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 compile or + 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 @@ -282,23 +295,31 @@ of these things: Library will not necessarily be able to recompile the application to use the modified definitions.) - b) Accompany the work with a written offer, valid for at + 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. - c) If distribution of the work is made by offering access to copy + 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. - d) Verify that the user has already received a copy of these + 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 source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major +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. @@ -347,7 +368,7 @@ 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 to +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 @@ -390,7 +411,7 @@ 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 Library General Public License from time to time. +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. @@ -453,16 +474,16 @@ convey the exclusion of warranty; and each file should have at least the Copyright (C) This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public + modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + 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 - Library General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU Library General Public + 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 From 0c1603debac440978c1d55c46cb11059f8350b35 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 21 Feb 2017 09:30:19 -0500 Subject: [PATCH 100/280] tests/xattrs: Fix possible NULL allocation This showed up in the ostree runs with `-fsanitize=undefined` - if we happened to get `0` then `g_malloc` would return `NULL`. However, what's interesting is it seemed to happen *consistently*. I think what's going on is GCC proved that the value *could* be zero, and hence it *could* return NULL, and hence it was undefined behavior. Hooray for `-fsanitize=undefined`. --- tests/test-libglnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 9a398701..21bdbdb0 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -42,7 +42,7 @@ static gboolean set_random_xattr_value (int fd, const char *name, GError **error) { const guint8 randxattrbyte = g_random_int_range (0, 256); - const guint32 randxattrvalue_len = g_random_int () % 256; /* Picked to be not too small or large */ + const guint32 randxattrvalue_len = (g_random_int () % 256) + 1; /* Picked to be not too small or large */ g_autofree char *randxattrvalue = g_malloc (randxattrvalue_len); memset (randxattrvalue, randxattrbyte, randxattrvalue_len); From 5309e363aa30d2108a264ae35d8d870ee3e0c443 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Thu, 2 Mar 2017 13:55:30 -0500 Subject: [PATCH 101/280] fix bug found by -Wmaybe-uninitialized --- tests/test-libglnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 21bdbdb0..0b21133a 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -192,7 +192,7 @@ xattr_thread (gpointer data) GError **error = &local_error; struct XattrWorker *worker = data; guint64 end_time = g_get_monotonic_time () + XATTR_THREAD_RUN_TIME_USECS; - guint n_read; + guint n_read = 0; while (g_get_monotonic_time () < end_time) { From c83ec7f213bd2e435043a435906e46aa9c0a2b6a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 1 Mar 2017 22:13:14 -0500 Subject: [PATCH 102/280] fdio: Expose wrappers for renameat2() EXCHANGE and NOREPLACE I want the `RENAME_EXCHANGE` version for rpm-ostree, to atomically swap `/usr/share/rpm` (a directory) with a new verison. While we're here we might as well expose `RENAME_NOREPLACE` in case something else wants it. These both have fallbacks to the non-atomic version. Closes: https://github.com/GNOME/libglnx/pull/36 --- Makefile-libglnx.am | 9 ++- glnx-fdio.c | 85 ++++++++++++++++----- glnx-fdio.h | 6 ++ glnx-missing.h | 3 + tests/test-libglnx-fdio.c | 155 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 236 insertions(+), 22 deletions(-) create mode 100644 tests/test-libglnx-fdio.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index d3a46e55..dfe65263 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -52,9 +52,14 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) -TESTS += test-libglnx-xattrs +libglnx_tests = test-libglnx-xattrs test-libglnx-fdio +TESTS += $(libglnx_tests) -check_PROGRAMS += test-libglnx-xattrs +check_PROGRAMS += $(libglnx_tests) test_libglnx_xattrs_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-xattrs.c test_libglnx_xattrs_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_xattrs_LDADD = $(libglnx_libs) libglnx.la + +test_libglnx_fdio_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-fdio.c +test_libglnx_fdio_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la diff --git a/glnx-fdio.c b/glnx-fdio.c index 7ee57cd6..68704cb1 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -54,11 +54,13 @@ sizeof(type) <= 4 ? 10 : \ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) -static gboolean -rename_file_noreplace_at (int olddirfd, const char *oldpath, - int newdirfd, const char *newpath, - gboolean ignore_eexist, - GError **error) + +/* An implementation of renameat2(..., RENAME_NOREPLACE) + * with fallback to a non-atomic version. + */ +int +glnx_renameat2_noreplace (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) { #ifndef ENABLE_WRPSEUDO_COMPAT if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0) @@ -66,17 +68,10 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, if (errno == EINVAL || errno == ENOSYS) { /* Fall through */ - ; - } - else if (errno == EEXIST && ignore_eexist) - { - (void) unlinkat (olddirfd, oldpath, 0); - return TRUE; } else { - glnx_set_error_from_errno (error); - return FALSE; + return -1; } } else @@ -84,24 +79,74 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, #endif if (linkat (olddirfd, oldpath, newdirfd, newpath, 0) < 0) + return -1; + + if (unlinkat (olddirfd, oldpath, 0) < 0) + return -1; + + return 0; +} + +static gboolean +rename_file_noreplace_at (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, + gboolean ignore_eexist, + GError **error) +{ + if (glnx_renameat2_noreplace (olddirfd, oldpath, + newdirfd, newpath) < 0) { if (errno == EEXIST && ignore_eexist) - /* Fall through */ - ; + { + (void) unlinkat (olddirfd, oldpath, 0); + return TRUE; + } else { glnx_set_error_from_errno (error); return FALSE; } } - - if (unlinkat (olddirfd, oldpath, 0) < 0) + return TRUE; +} + +/* An implementation of renameat2(..., RENAME_EXCHANGE) + * with fallback to a non-atomic version. + */ +int +glnx_renameat2_exchange (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) +{ +#ifndef ENABLE_WRPSEUDO_COMPAT + if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_EXCHANGE) == 0) + return 0; + else { - glnx_set_error_from_errno (error); - return FALSE; + if (errno == ENOSYS || errno == EINVAL) + { + /* Fall through */ + } + else + { + return -1; + } } +#endif - return TRUE; + /* Fallback */ + { const char *old_tmp_name = glnx_strjoina (oldpath, ".XXXXXX"); + + /* Move old out of the way */ + if (renameat (olddirfd, oldpath, olddirfd, old_tmp_name) < 0) + return -1; + /* Now move new into its place */ + if (renameat (newdirfd, newpath, olddirfd, oldpath) < 0) + return -1; + /* And finally old(tmp) into new */ + if (renameat (olddirfd, old_tmp_name, newdirfd, newpath) < 0) + return -1; + } + return 0; } gboolean diff --git a/glnx-fdio.h b/glnx-fdio.h index 111df9d5..c3e7573c 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -150,4 +150,10 @@ glnx_stream_fstat (GFileDescriptorBased *stream, struct stat *stbuf, GError **error); +int glnx_renameat2_noreplace (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath); +int glnx_renameat2_exchange (int olddirfd, const char *oldpath, + int newdirfd, const char *newpath); + + G_END_DECLS diff --git a/glnx-missing.h b/glnx-missing.h index fa80d3e8..a60705a1 100644 --- a/glnx-missing.h +++ b/glnx-missing.h @@ -48,5 +48,8 @@ #ifndef RENAME_NOREPLACE #define RENAME_NOREPLACE (1 << 0) #endif +#ifndef RENAME_EXCHANGE +#define RENAME_EXCHANGE (1 << 1) +#endif #include "glnx-missing-syscall.h" diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c new file mode 100644 index 00000000..9830c107 --- /dev/null +++ b/tests/test-libglnx-fdio.c @@ -0,0 +1,155 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Red Hat, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx.h" +#include +#include +#include +#include +#include + +static gboolean +renameat_test_setup (int *out_srcfd, int *out_destfd, + GError **error) +{ + glnx_fd_close int srcfd = -1; + glnx_fd_close int destfd = -1; + + (void) glnx_shutil_rm_rf_at (AT_FDCWD, "srcdir", NULL, NULL); + if (mkdir ("srcdir", 0755) < 0) + err (1, "mkdir"); + if (!glnx_opendirat (AT_FDCWD, "srcdir", TRUE, &srcfd, error)) + return FALSE; + (void) glnx_shutil_rm_rf_at (AT_FDCWD, "destdir", NULL, NULL); + if (mkdir ("destdir", 0755) < 0) + err (1, "mkdir"); + if (!glnx_opendirat (AT_FDCWD, "destdir", TRUE, &destfd, error)) + return FALSE; + + if (!glnx_file_replace_contents_at (srcfd, "foo", (guint8*)"foo contents", strlen ("foo contents"), + GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) + return FALSE; + if (!glnx_file_replace_contents_at (destfd, "bar", (guint8*)"bar contents", strlen ("bar contents"), + GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) + return FALSE; + + *out_srcfd = srcfd; srcfd = -1; + *out_destfd = destfd; destfd = -1; + return TRUE; +} + +static void +test_renameat2_noreplace (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + glnx_fd_close int srcfd = -1; + glnx_fd_close int destfd = -1; + struct stat stbuf; + + if (!renameat_test_setup (&srcfd, &destfd, error)) + goto out; + + if (glnx_renameat2_noreplace (srcfd, "foo", destfd, "bar") == 0) + g_assert_not_reached (); + else + { + g_assert_cmpint (errno, ==, EEXIST); + } + + if (glnx_renameat2_noreplace (srcfd, "foo", destfd, "baz") < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + if (fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) == 0) + g_assert_not_reached (); + else + g_assert_cmpint (errno, ==, ENOENT); + + out: + g_assert_no_error (local_error); +} + +static void +test_renameat2_exchange (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + glnx_fd_close int srcfd = -1; + glnx_fd_close int destfd = -1; + struct stat stbuf; + + if (!renameat_test_setup (&srcfd, &destfd, error)) + goto out; + + if (glnx_renameat2_exchange (AT_FDCWD, "srcdir", AT_FDCWD, "destdir") < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + /* Ensure the dir fds are the same */ + if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + if (fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + /* But the dirs should be swapped */ + if (fstatat (AT_FDCWD, "destdir/foo", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + if (fstatat (AT_FDCWD, "srcdir/bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + out: + g_assert_no_error (local_error); +} + +int main (int argc, char **argv) +{ + int ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); + g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); + + ret = g_test_run(); + + return ret; +} From 074236b88d0c742965fcca25a235fc79d3f87e48 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 21 Mar 2017 10:23:37 -0400 Subject: [PATCH 103/280] errors: Add new glnx_throw_errno{,_prefix}() APIs We have a *lot* of code of the form: ``` if (unlinkat (fd, pathname) < 0) { glnx_set_error_from_errno (error); goto out; } ``` After conversion to `return FALSE style` which is in progress, it's way shorter, and clearer like this: ``` if (unlinkat (fd, pathname) < 0) return glnx_throw_errno (error); ``` --- glnx-dirfd.c | 6 ++-- glnx-errors.c | 35 +++++++++-------------- glnx-errors.h | 79 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 11388c18..08d90071 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -68,8 +68,7 @@ glnx_opendirat (int dfd, int ret = glnx_opendirat_with_errno (dfd, path, follow); if (ret == -1) { - glnx_set_prefix_error_from_errno (error, "%s", "openat"); - return FALSE; + return glnx_throw_errno_prefix (error, "openat"); } *out_fd = ret; return TRUE; @@ -339,8 +338,7 @@ glnx_mkdtempat (int dfd, /* Any other error will apply also to other names we might * try, and there are 2^32 or so of them, so give up now. */ - glnx_set_prefix_error_from_errno (error, "%s", "mkdirat"); - return FALSE; + return glnx_throw_errno_prefix (error, "mkdirat"); } return TRUE; diff --git a/glnx-errors.c b/glnx-errors.c index ab7bc0aa..edbce205 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -24,30 +24,21 @@ #include void -glnx_real_set_prefix_error_from_errno (GError **error, - gint errsv, - const char *format, - ...) +glnx_real_set_prefix_error_from_errno_va (GError **error, + gint errsv, + const char *format, + va_list args) { if (!error) return; - else - { - GString *buf = g_string_new (""); - va_list args; - - va_start (args, format); - g_string_append_vprintf (buf, format, args); - va_end (args); - g_string_append (buf, ": "); - g_string_append (buf, g_strerror (errsv)); - - g_set_error_literal (error, - G_IO_ERROR, - g_io_error_from_errno (errsv), - buf->str); - g_string_free (buf, TRUE); - errno = errsv; - } + /* TODO - enhance GError to have a "set and take ownership" API */ + g_autoptr(GString) buf = g_string_new (""); + g_string_append_vprintf (buf, format, args); + g_string_append (buf, ": "); + g_string_append (buf, g_strerror (errsv)); + g_set_error_literal (error, + G_IO_ERROR, + g_io_error_from_errno (errsv), + buf->str); } diff --git a/glnx-errors.h b/glnx-errors.h index fffef6c9..7bf53d3e 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -25,25 +25,76 @@ G_BEGIN_DECLS +/* Set @error using the value of `g_strerror (errno)`. + * + * This function returns %FALSE so it can be used conveniently in a single + * statement: + * + * `` + * if (unlinkat (fd, somepathname) < 0) + * return glnx_throw_errno (error); + * ``` + */ +static inline gboolean +glnx_throw_errno (GError **error) +{ + /* Save the value of errno, in case one of the + * intermediate function calls happens to set it. + */ + int errsv = errno; + g_set_error_literal (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + g_strerror (errsv)); + /* We also restore the value of errno, since that's + * what was done in a long-ago libgsystem commit + * https://git.gnome.org/browse/libgsystem/commit/?id=ed106741f7a0596dc8b960b31fdae671d31d666d + * but I certainly can't remember now why I did that. + */ + errno = errsv; + return FALSE; +} + +/* Implementation detail of glnx_throw_errno_prefix() */ +void glnx_real_set_prefix_error_from_errno_va (GError **error, + gint errsv, + const char *format, + va_list args) G_GNUC_PRINTF (3,0); + +/* Set @error using the value of `$prefix: g_strerror (errno)` where `$prefix` + * is computed via printf @fmt. + * + * This function returns %FALSE so it can be used conveniently in a single + * statement: + * + * ``` + * return glnx_throw_errno_prefix (error, "unlinking %s", pathname); + * ``` + */ +static inline gboolean G_GNUC_PRINTF (2,3) +glnx_throw_errno_prefix (GError **error, const char *fmt, ...) +{ + int errsv = errno; + va_list args; + va_start (args, fmt); + glnx_real_set_prefix_error_from_errno_va (error, errsv, fmt, args); + va_end (args); + /* See comment above about preserving errno */ + errno = errsv; + return FALSE; +} + +/* BEGIN LEGACY APIS */ + #define glnx_set_error_from_errno(error) \ do { \ - int errsv = errno; \ - g_set_error_literal (error, G_IO_ERROR, \ - g_io_error_from_errno (errsv), \ - g_strerror (errsv)); \ - errno = errsv; \ + glnx_throw_errno (error); \ } while (0); -#define glnx_set_prefix_error_from_errno(error, format, args...) \ - do { \ - int errsv = errno; \ - glnx_real_set_prefix_error_from_errno (error, errsv, format, args); \ - errno = errsv; \ +#define glnx_set_prefix_error_from_errno(error, format, args...) \ + do { \ + glnx_set_error_from_errno (error); \ + g_prefix_error (error, format, args); \ } while (0); -void glnx_real_set_prefix_error_from_errno (GError **error, - gint errsv, - const char *format, - ...) G_GNUC_PRINTF (3,4); G_END_DECLS From 602fdd93cb7a339c6b5749eee73df926429a5ab8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 21 Mar 2017 11:19:16 -0400 Subject: [PATCH 104/280] errors: Add glnx_throw() and tests Following up to the previous commit, also shorten our use of `g_set_error (..., G_IO_ERROR_FAILED, ...)`. There's a lot of this in libostree at least. See also https://bugzilla.gnome.org/show_bug.cgi?id=774061 --- Makefile-libglnx.am | 6 ++- glnx-errors.h | 25 ++++++++++++ tests/test-libglnx-errors.c | 79 +++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 tests/test-libglnx-errors.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index dfe65263..805a9d04 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -52,7 +52,7 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) -libglnx_tests = test-libglnx-xattrs test-libglnx-fdio +libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors TESTS += $(libglnx_tests) check_PROGRAMS += $(libglnx_tests) @@ -63,3 +63,7 @@ test_libglnx_xattrs_LDADD = $(libglnx_libs) libglnx.la test_libglnx_fdio_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-fdio.c test_libglnx_fdio_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la + +test_libglnx_errors_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-errors.c +test_libglnx_errors_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la diff --git a/glnx-errors.h b/glnx-errors.h index 7bf53d3e..8f9cad81 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -25,6 +25,31 @@ G_BEGIN_DECLS +/* Set @error with G_IO_ERROR/G_IO_ERROR_FAILED. + * + * This function returns %FALSE so it can be used conveniently in a single + * statement: + * + * `` + * if (strcmp (foo, "somevalue") != 0) + * return glnx_throw (error, "key must be somevalue, not '%s'", foo); + * ``` + */ +static inline gboolean G_GNUC_PRINTF (2,3) +glnx_throw (GError **error, const char *fmt, ...) +{ + if (error == NULL) + return FALSE; + + va_list args; + va_start (args, fmt); + GError *new = g_error_new_valist (G_IO_ERROR, G_IO_ERROR_FAILED, fmt, args); + va_end (args); + g_propagate_error (error, g_steal_pointer (&new)); + return FALSE; +} + + /* Set @error using the value of `g_strerror (errno)`. * * This function returns %FALSE so it can be used conveniently in a single diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c new file mode 100644 index 00000000..8d109cd1 --- /dev/null +++ b/tests/test-libglnx-errors.c @@ -0,0 +1,79 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Red Hat, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx.h" +#include +#include +#include +#include + +static void +test_error_throw (void) +{ + g_autoptr(GError) error = NULL; + + g_assert (!glnx_throw (&error, "foo: %s %d", "hello", 42)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_cmpstr (error->message, ==, "foo: hello 42"); +} + +static void +test_error_errno (void) +{ + g_autoptr(GError) error = NULL; + const char noent_path[] = "/enoent-this-should-not-exist"; + int fd; + + fd = open (noent_path, O_RDONLY); + if (fd < 0) + { + g_assert (!glnx_throw_errno (&error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&error); + } + else + g_assert_cmpint (fd, ==, -1); + + fd = open (noent_path, O_RDONLY); + if (fd < 0) + { + g_assert (!glnx_throw_errno_prefix (&error, "Failed to open %s", noent_path)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, glnx_strjoina ("Failed to open ", noent_path))); + g_clear_error (&error); + } + else + g_assert_cmpint (fd, ==, -1); +} + +int main (int argc, char **argv) +{ + int ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/error-throw", test_error_throw); + g_test_add_func ("/error-errno", test_error_errno); + + ret = g_test_run(); + + return ret; +} From 0c52d85e69310c6499a70a360a03fba2b4532677 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Thu, 2 Mar 2017 13:55:30 -0500 Subject: [PATCH 105/280] glnx-errors.h: add glnx_null_throw[_*] variants These are equivalent to the non-null throw, except that the returned value is a NULL pointer. They can be used in functions where one wants to return a pointer. E.g.: GKeyFile *foo(GError **error) { return glnx_null_throw (error, "foobar"); } The function call redirections are wrapped around a compound statement expression[1] so that they represent a single top-level expression. This allows us to avoid -Wunused-value warnings vs using a comma operator if the return value isn't used. I made the 'args...' absorb the fmt argument as well so that callers can still use it without always having to specify at least one additional variadic argument. I had to check to be sure that the expansion is all done by the preprocessor, so we don't need to worry about stack intricacies. [1] https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html --- glnx-errors.h | 11 +++++++++ glnx-fdio.c | 11 +++------ tests/test-libglnx-errors.c | 48 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/glnx-errors.h b/glnx-errors.h index 8f9cad81..b891f963 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -49,6 +49,9 @@ glnx_throw (GError **error, const char *fmt, ...) return FALSE; } +/* Like glnx_throw(), but yields a NULL pointer. */ +#define glnx_null_throw(error, args...) \ + ({glnx_throw (error, args); NULL;}) /* Set @error using the value of `g_strerror (errno)`. * @@ -79,6 +82,10 @@ glnx_throw_errno (GError **error) return FALSE; } +/* Like glnx_throw_errno(), but yields a NULL pointer. */ +#define glnx_null_throw_errno(error) \ + ({glnx_throw_errno (error); NULL;}) + /* Implementation detail of glnx_throw_errno_prefix() */ void glnx_real_set_prefix_error_from_errno_va (GError **error, gint errsv, @@ -108,6 +115,10 @@ glnx_throw_errno_prefix (GError **error, const char *fmt, ...) return FALSE; } +/* Like glnx_throw_errno_prefix(), but yields a NULL pointer. */ +#define glnx_null_throw_errno_prefix(error, args...) \ + ({glnx_throw_errno_prefix (error, args); NULL;}) + /* BEGIN LEGACY APIS */ #define glnx_set_error_from_errno(error) \ diff --git a/glnx-fdio.c b/glnx-fdio.c index 68704cb1..de3955ce 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -555,25 +555,20 @@ glnx_readlinkat_malloc (int dfd, for (;;) { - char *c; + g_autofree char *c = NULL; ssize_t n; c = g_malloc (l); n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); if (n < 0) - { - glnx_set_error_from_errno (error); - g_free (c); - return FALSE; - } + return glnx_null_throw_errno (error); if ((size_t) n < l-1) { c[n] = 0; - return c; + return g_steal_pointer (&c); } - g_free (c); l *= 2; } diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 8d109cd1..7c7459cb 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -33,6 +33,19 @@ test_error_throw (void) g_assert (!glnx_throw (&error, "foo: %s %d", "hello", 42)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); g_assert_cmpstr (error->message, ==, "foo: hello 42"); + g_clear_error (&error); + + gpointer dummy = glnx_null_throw (&error, "literal foo"); + g_assert (dummy == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_cmpstr (error->message, ==, "literal foo"); + g_clear_error (&error); + + gpointer dummy2 = glnx_null_throw (&error, "foo: %s %d", "hola", 24); + g_assert (dummy2 == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); + g_assert_cmpstr (error->message, ==, "foo: hola 24"); + g_clear_error (&error); } static void @@ -52,6 +65,17 @@ test_error_errno (void) else g_assert_cmpint (fd, ==, -1); + fd = open (noent_path, O_RDONLY); + if (fd < 0) + { + gpointer dummy = glnx_null_throw_errno (&error); + g_assert (dummy == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&error); + } + else + g_assert_cmpint (fd, ==, -1); + fd = open (noent_path, O_RDONLY); if (fd < 0) { @@ -62,6 +86,30 @@ test_error_errno (void) } else g_assert_cmpint (fd, ==, -1); + + fd = open (noent_path, O_RDONLY); + if (fd < 0) + { + gpointer dummy = glnx_null_throw_errno_prefix (&error, "Failed to open file"); + g_assert (dummy == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, "Failed to open file")); + g_clear_error (&error); + } + else + g_assert_cmpint (fd, ==, -1); + + fd = open (noent_path, O_RDONLY); + if (fd < 0) + { + gpointer dummy = glnx_null_throw_errno_prefix (&error, "Failed to open %s", noent_path); + g_assert (dummy == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, glnx_strjoina ("Failed to open ", noent_path))); + g_clear_error (&error); + } + else + g_assert_cmpint (fd, ==, -1); } int main (int argc, char **argv) From 4040f55ac516ef2473884928a2b9d22efa96cca3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 24 Mar 2017 10:21:22 -0400 Subject: [PATCH 106/280] errors: Fix legacy set_prefix_error_from_errno() We were missing the previous automatic `: ` addition; noticed in a failing ostree test. Fix this by just calling the new API as the non-prefix case does too. --- glnx-errors.h | 4 +--- tests/test-libglnx-errors.c | 8 +++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/glnx-errors.h b/glnx-errors.h index b891f963..2f6b9628 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -128,9 +128,7 @@ glnx_throw_errno_prefix (GError **error, const char *fmt, ...) #define glnx_set_prefix_error_from_errno(error, format, args...) \ do { \ - glnx_set_error_from_errno (error); \ - g_prefix_error (error, format, args); \ + glnx_throw_errno_prefix (error, format, args); \ } while (0); - G_END_DECLS diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 7c7459cb..7950244f 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -79,9 +79,15 @@ test_error_errno (void) fd = open (noent_path, O_RDONLY); if (fd < 0) { + g_autofree char *expected_prefix = g_strdup_printf ("Failed to open %s", noent_path); g_assert (!glnx_throw_errno_prefix (&error, "Failed to open %s", noent_path)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); - g_assert (g_str_has_prefix (error->message, glnx_strjoina ("Failed to open ", noent_path))); + g_assert (g_str_has_prefix (error->message, expected_prefix)); + g_clear_error (&error); + /* And test the legacy wrapper */ + glnx_set_prefix_error_from_errno (&error, "Failed to open %s", noent_path); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, expected_prefix)); g_clear_error (&error); } else From 2576a07e6e5a400501b126e0c73b042519bb5001 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 20 Apr 2017 16:58:52 +0100 Subject: [PATCH 107/280] glnx-local-alloc: Make check for invalid FDs more general In general, all FDs < 0 are invalid (and should not have close() called on them), so check that. This could have caused problems if a function returned an error value < -1. Signed-off-by: Philip Withnall --- glnx-local-alloc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index f628b61c..8c1914cf 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -199,9 +199,9 @@ glnx_cleanup_close_fdp (int *fdp) int fd, errsv; g_assert (fdp); - + fd = *fdp; - if (fd != -1) + if (fd >= 0) { errsv = errno; (void) close (fd); From 9307f51893702e9bdd2292e46de64e9e5aecb50f Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 20 Apr 2017 17:03:24 +0100 Subject: [PATCH 108/280] glnx-shutil: Add glnx_shutil_mkdir_p_at_open() This is a variant of glnx_shutil_mkdir_p_at() which opens the given directory and returns a dirfd to it. Currently, the implementation cannot be race-free (due to a kernel bug), but it could eventually be made race-free. Signed-off-by: Philip Withnall --- glnx-shutil.c | 37 +++++++++++++++++++++++++++++++++++++ glnx-shutil.h | 8 ++++++++ 2 files changed, 45 insertions(+) diff --git a/glnx-shutil.c b/glnx-shutil.c index 6ff9fa68..4c62ba21 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -246,3 +246,40 @@ glnx_shutil_mkdir_p_at (int dfd, out: return ret; } + +/** + * glnx_shutil_mkdir_p_at_open: + * @dfd: Directory fd + * @path: Directory path to be created + * @mode: Mode for newly created directories + * @out_dfd: (out caller-allocates): Return location for an FD to @dfd/@path, + * or `-1` on error + * @cancellable: (nullable): Cancellable, or %NULL + * @error: Return location for a #GError, or %NULL + * + * Similar to glnx_shutil_mkdir_p_at(), except it opens the resulting directory + * and returns a directory FD to it. Currently, this is not guaranteed to be + * race-free. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +gboolean +glnx_shutil_mkdir_p_at_open (int dfd, + const char *path, + int mode, + int *out_dfd, + GCancellable *cancellable, + GError **error) +{ + /* FIXME: It’s not possible to eliminate the race here until + * openat(O_DIRECTORY | O_CREAT) works (and returns a directory rather than a + * file). It appears to be not supported in current kernels. (Tested with + * 4.10.10-200.fc25.x86_64.) */ + *out_dfd = -1; + + if (!glnx_shutil_mkdir_p_at (dfd, path, mode, cancellable, error)) + return FALSE; + + return glnx_opendirat (dfd, path, TRUE, out_dfd, error); +} diff --git a/glnx-shutil.h b/glnx-shutil.h index 8cc732c3..56a99fa2 100644 --- a/glnx-shutil.h +++ b/glnx-shutil.h @@ -37,4 +37,12 @@ glnx_shutil_mkdir_p_at (int dfd, GCancellable *cancellable, GError **error); +gboolean +glnx_shutil_mkdir_p_at_open (int dfd, + const char *path, + int mode, + int *out_dfd, + GCancellable *cancellable, + GError **error); + G_END_DECLS From 6746e6f54d6d29066011bace3b7820e9ca240ac6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 20 Apr 2017 18:01:56 +0100 Subject: [PATCH 109/280] glnx-dirfd: Add variants of glnx_mkdtempat() which open the directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the moment, it’s not possible for them to do this race-free (since openat(O_DIRECTORY | O_CREAT | O_EXCL) doesn’t work), but in future this could be possible. In any case, it’s a useful thing to want to do. Signed-off-by: Philip Withnall --- glnx-dirfd.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-dirfd.h | 11 +++++++++ 2 files changed, 78 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 08d90071..8c437205 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -348,3 +348,70 @@ glnx_mkdtempat (int dfd, "mkstempat ran out of combinations to try."); return FALSE; } + +/** + * glnx_mkdtempat_open: + * @dfd: Directory FD + * @tmpl: (type filename): template directory name, last 6 characters will be replaced + * @mode: permissions to create the temporary directory with + * @out_dfd: (out caller-allocates): Return location for an FD for the new + * temporary directory, or `-1` on error + * @error: Return location for a #GError, or %NULL + * + * Similar to glnx_mkdtempat(), except it will open the resulting temporary + * directory and return a directory FD to it. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +gboolean +glnx_mkdtempat_open (int dfd, + gchar *tmpl, + int mode, + int *out_dfd, + GError **error) +{ + /* FIXME: Ideally we could use openat(O_DIRECTORY | O_CREAT | O_EXCL) here + * to create and open the directory atomically, but that’s not supported by + * current kernel versions: http://www.openwall.com/lists/oss-security/2014/11/26/14 + * (Tested on kernel 4.10.10-200.fc25.x86_64). For the moment, accept a + * TOCTTOU race here. */ + *out_dfd = -1; + + if (!glnx_mkdtempat (dfd, tmpl, mode, error)) + return FALSE; + + return glnx_opendirat (dfd, tmpl, FALSE, out_dfd, error); +} + +/** + * glnx_mkdtempat_open_in_system: + * @tmpl: (type filename): template directory name, last 6 characters will be replaced + * @mode: permissions to create the temporary directory with + * @out_dfd: (out caller-allocates): Return location for an FD for the new + * temporary directory, or `-1` on error + * @error: Return location for a #GError, or %NULL + * + * Similar to glnx_mkdtempat_open(), except it will use the system temporary + * directory (from g_get_tmp_dir()) as the parent directory to @tmpl. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +gboolean +glnx_mkdtempat_open_in_system (gchar *tmpl, + int mode, + int *out_dfd, + GError **error) +{ + glnx_fd_close int tmp_dfd = -1; + + *out_dfd = -1; + + if (!glnx_opendirat (-1, g_get_tmp_dir (), TRUE, &tmp_dfd, error)) + return FALSE; + + return glnx_mkdtempat_open (tmp_dfd, tmpl, mode, out_dfd, error); +} + + diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 3a766952..0cb79e69 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -88,4 +88,15 @@ gboolean glnx_mkdtempat (int dfd, int mode, GError **error); +gboolean glnx_mkdtempat_open (int dfd, + gchar *tmpl, + int mode, + int *out_dfd, + GError **error); + +gboolean glnx_mkdtempat_open_in_system (gchar *tmpl, + int mode, + int *out_dfd, + GError **error); + G_END_DECLS From 2b82858169186d2758c5fe60ad1099eab7f46a25 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 21 Apr 2017 13:14:47 +0100 Subject: [PATCH 110/280] glnx-fdio: Add wrappers around fstat() and fstatat() to handle errors Add two inline wrappers around fstat() and fstatat() which handle retrying on EINTR and return other errors using GError, to be consistent with other glnx functions. Signed-off-by: Philip Withnall --- glnx-fdio.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index c3e7573c..56d0b78e 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -35,6 +35,8 @@ #include #undef basename +#include + G_BEGIN_DECLS /* Irritatingly, g_basename() which is what we want @@ -155,5 +157,54 @@ int glnx_renameat2_noreplace (int olddirfd, const char *oldpath, int glnx_renameat2_exchange (int olddirfd, const char *oldpath, int newdirfd, const char *newpath); +/** + * glnx_fstat: + * @fd: FD to stat + * @buf: (out caller-allocates): Return location for stat details + * @error: Return location for a #GError, or %NULL + * + * Wrapper around fstat() which adds #GError support and ensures that it retries + * on %EINTR. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +static inline gboolean +glnx_fstat (int fd, + struct stat *buf, + GError **error) +{ + if (TEMP_FAILURE_RETRY (fstat (fd, buf)) != 0) + return glnx_throw_errno (error); + + return TRUE; +} + +/** + * glnx_fstatat: + * @dfd: Directory FD to stat beneath + * @path: Path to stat beneath @dfd + * @buf: (out caller-allocates): Return location for stat details + * @flags: Flags to pass to fstatat() + * @error: Return location for a #GError, or %NULL + * + * Wrapper around fstatat() which adds #GError support and ensures that it + * retries on %EINTR. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +static inline gboolean +glnx_fstatat (int dfd, + const gchar *path, + struct stat *buf, + int flags, + GError **error) +{ + if (TEMP_FAILURE_RETRY (fstatat (dfd, path, buf, flags)) != 0) + return glnx_throw_errno (error); + + return TRUE; +} G_END_DECLS From 74383ba40529ef05346f617c846b55a73da1810c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Apr 2017 17:47:15 -0400 Subject: [PATCH 111/280] tests/xattrs: Skip on filesystems with no user xattr support Like tmpfs. See: https://github.com/flatpak/flatpak/issues/686 --- tests/test-libglnx-xattrs.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 0b21133a..b6f0ac69 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -244,6 +244,21 @@ test_xattr_races (void) if (!glnx_opendirat (AT_FDCWD, tmpdir, TRUE, &dfd, error)) goto out; + /* Support people building/testing on tmpfs https://github.com/flatpak/flatpak/issues/686 */ + if (fsetxattr (dfd, "user.test", "novalue", strlen ("novalue"), 0) < 0) + { + if (errno == EOPNOTSUPP) + { + g_test_skip ("no xattr support"); + return; + } + else + { + glnx_set_error_from_errno (error); + goto out; + } + } + for (guint i = 0; i < nprocs; i++) { struct XattrWorker *worker = &wdata[i]; From 47fafa97e97eb4fcf32bf73db0cb3752e510b8f3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 25 Apr 2017 10:30:05 -0400 Subject: [PATCH 112/280] Port most code (except fdio) to new style There's a lot more fdio code, starting with some of the easier ones. --- glnx-dirfd.c | 12 ++------ glnx-lockfile.c | 23 +++++---------- glnx-shutil.c | 76 ++++++++++++++----------------------------------- 3 files changed, 31 insertions(+), 80 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 8c437205..12b983c1 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -209,13 +209,12 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; struct dirent *ret_dent; g_return_val_if_fail (out_dent, FALSE); if (!glnx_dirfd_iterator_next_dent (dfd_iter, out_dent, cancellable, error)) - goto out; + return FALSE; ret_dent = *out_dent; @@ -226,17 +225,12 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, { struct stat stbuf; if (TEMP_FAILURE_RETRY (fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno (error); ret_dent->d_type = IFTODT (stbuf.st_mode); } } - ret = TRUE; - out: - return ret; + return TRUE; } /** diff --git a/glnx-lockfile.c b/glnx-lockfile.c index 7dd69e91..c1cfc6b2 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -61,7 +61,6 @@ */ gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) { - gboolean ret = FALSE; glnx_fd_close int fd = -1; g_autofree char *t = NULL; int r; @@ -89,10 +88,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc struct stat st; fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) { - glnx_set_error_from_errno(error); - goto out; - } + if (fd < 0) + return glnx_throw_errno(error); /* Unfortunately, new locks are not in RHEL 7.1 glibc */ #ifdef F_OFD_SETLK @@ -107,10 +104,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc if (errno == EINVAL) r = flock(fd, operation); - if (r < 0) { - glnx_set_error_from_errno(error); - goto out; - } + if (r < 0) + return glnx_throw_errno(error); } /* If we acquired the lock, let's check if the file @@ -120,10 +115,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc * hence try again. */ r = fstat(fd, &st); - if (r < 0) { - glnx_set_error_from_errno(error); - goto out; - } + if (r < 0) + return glnx_throw_errno(error); if (st.st_nlink > 0) break; @@ -142,9 +135,7 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc fd = -1; t = NULL; - ret = TRUE; - out: - return ret; + return TRUE; } void glnx_release_lock_file(GLnxLockFile *f) { diff --git a/glnx-shutil.c b/glnx-shutil.c index 4c62ba21..6a1cdd4a 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -31,14 +31,12 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; struct dirent *dent; while (TRUE) { if (!glnx_dirfd_iterator_next_dent_ensure_dtype (dfd_iter, &dent, cancellable, error)) - goto out; - + return FALSE; if (dent == NULL) break; @@ -48,33 +46,25 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, if (!glnx_dirfd_iterator_init_at (dfd_iter->fd, dent->d_name, FALSE, &child_dfd_iter, error)) - goto out; + return FALSE; if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error)) - goto out; + return FALSE; if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno_prefix (error, "unlinkat"); } else { if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1) { if (errno != ENOENT) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno_prefix (error, "unlinkat"); } } } - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -94,7 +84,6 @@ glnx_shutil_rm_rf_at (int dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; glnx_fd_close int target_dfd = -1; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; @@ -114,40 +103,28 @@ glnx_shutil_rm_rf_at (int dfd, else if (errsv == ENOTDIR || errsv == ELOOP) { if (unlinkat (dfd, path, 0) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno_prefix (error, "unlinkat"); } else - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno_prefix (error, "open(%s)", path); } else { if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error)) - goto out; + return FALSE; target_dfd = -1; if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error)) - goto out; + return FALSE; if (unlinkat (dfd, path, AT_REMOVEDIR) == -1) { - int errsv = errno; - if (errsv != ENOENT) - { - glnx_set_error_from_errno (error); - goto out; - } + if (errno != ENOENT) + return glnx_throw_errno_prefix (error, "unlinkat"); } } - ret = TRUE; - out: - return ret; + return TRUE; } static gboolean @@ -157,11 +134,10 @@ mkdir_p_at_internal (int dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; gboolean did_recurse = FALSE; if (g_cancellable_set_error_if_cancelled (cancellable, error)) - goto out; + return FALSE; again: if (mkdirat (dfd, path, mode) == -1) @@ -179,7 +155,7 @@ mkdir_p_at_internal (int dfd, if (!glnx_shutil_mkdir_p_at (dfd, path, mode, cancellable, error)) - goto out; + return FALSE; /* Now restore it for another mkdir attempt */ *lastslash = '/'; @@ -194,15 +170,10 @@ mkdir_p_at_internal (int dfd, */ } else - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno_prefix (error, "mkdir(%s)", path); } - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -223,28 +194,23 @@ glnx_shutil_mkdir_p_at (int dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; struct stat stbuf; char *buf; /* Fast path stat to see whether it already exists */ if (fstatat (dfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) == 0) { + /* Note early return */ if (S_ISDIR (stbuf.st_mode)) - { - ret = TRUE; - goto out; - } + return TRUE; } buf = strdupa (path); if (!mkdir_p_at_internal (dfd, buf, mode, cancellable, error)) - goto out; + return FALSE; - ret = TRUE; - out: - return ret; + return TRUE; } /** From dc1956b2890bba7d5b20f1066241d07bece4fb0c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 25 Apr 2017 10:48:52 -0400 Subject: [PATCH 113/280] fdio: Mostly port to new code style There's one function that did `unlinkat()` in the cleanup section, not doing that yet. Note I uncovered a few bugs in a few places where we didn't preserve errno before doing an `unlinkat()` in error paths in a few cases. I also tried to prefix a few more error cases with the system call name. --- glnx-fdio.c | 208 +++++++++++++++------------------------------------- 1 file changed, 59 insertions(+), 149 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index de3955ce..cda627a0 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -102,10 +102,7 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, return TRUE; } else - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno (error); } return TRUE; } @@ -172,10 +169,7 @@ glnx_open_tmpfile_linkable_at (int dfd, #if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT) fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP)) - { - glnx_set_prefix_error_from_errno (error, "%s", "open(O_TMPFILE)"); - return FALSE; - } + return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); if (fd != -1) { *out_fd = fd; @@ -199,10 +193,7 @@ glnx_open_tmpfile_linkable_at (int dfd, if (errno == EEXIST) continue; else - { - glnx_set_prefix_error_from_errno (error, "%s", "Creating temp file"); - return FALSE; - } + return glnx_throw_errno_prefix (error, "Creating temp file"); } else { @@ -246,9 +237,10 @@ glnx_link_tmpfile_at (int dfd, */ if (renameat (dfd, tmpfile_path, target_dfd, target) < 0) { + int errsv = errno; (void) unlinkat (dfd, tmpfile_path, 0); - glnx_set_error_from_errno (error); - return FALSE; + errno = errsv; + return glnx_throw_errno_prefix (error, "renameat"); } } else @@ -293,10 +285,7 @@ glnx_link_tmpfile_at (int dfd, if (errno == EEXIST) continue; else - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno_prefix (error, "linkat"); } else break; @@ -311,9 +300,10 @@ glnx_link_tmpfile_at (int dfd, /* This is currently the only case where we need to have * a cleanup unlinkat() still with O_TMPFILE. */ + int errsv = errno; (void) unlinkat (target_dfd, tmpname_buf, 0); - glnx_set_error_from_errno (error); - return FALSE; + errno = errsv; + return glnx_throw_errno_prefix (error, "renameat"); } } else @@ -323,10 +313,7 @@ glnx_link_tmpfile_at (int dfd, if (errno == EEXIST && mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST) ; else - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno_prefix (error, "linkat"); } } @@ -341,49 +328,37 @@ glnx_fd_readall_malloc (int fd, GCancellable *cancellable, GError **error) { - gboolean success = FALSE; const guint maxreadlen = 4096; - int res; - struct stat stbuf; - guint8* buf = NULL; - gsize buf_allocated; - gsize buf_size = 0; - gssize bytes_read; - do - res = fstat (fd, &stbuf); - while (G_UNLIKELY (res == -1 && errno == EINTR)); - if (res == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + struct stat stbuf; + if (TEMP_FAILURE_RETRY (fstat (fd, &stbuf)) < 0) + return glnx_null_throw_errno (error); + gsize buf_allocated; if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0) buf_allocated = stbuf.st_size; else buf_allocated = 16; - - buf = g_malloc (buf_allocated); + g_autofree guint8* buf = g_malloc (buf_allocated); + + gsize buf_size = 0; while (TRUE) { gsize readlen = MIN (buf_allocated - buf_size, maxreadlen); - + if (g_cancellable_set_error_if_cancelled (cancellable, error)) - goto out; + return FALSE; + gssize bytes_read; do bytes_read = read (fd, buf + buf_size, readlen); while (G_UNLIKELY (bytes_read == -1 && errno == EINTR)); if (G_UNLIKELY (bytes_read == -1)) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_null_throw_errno (error); if (bytes_read == 0) break; - + buf_size += bytes_read; if (buf_allocated - buf_size < maxreadlen) buf = g_realloc (buf, buf_allocated *= 2); @@ -396,15 +371,8 @@ glnx_fd_readall_malloc (int fd, buf[buf_size] = '\0'; } - success = TRUE; - out: - if (success) - { - *out_len = buf_size; - return buf; - } - g_free (buf); - return NULL; + *out_len = buf_size; + return g_steal_pointer (&buf); } /** @@ -423,13 +391,10 @@ glnx_fd_readall_bytes (int fd, GCancellable *cancellable, GError **error) { - guint8 *buf; gsize len; - - buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error); + guint8 *buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error); if (!buf) return NULL; - return g_bytes_new_take (buf, len); } @@ -451,13 +416,10 @@ glnx_fd_readall_utf8 (int fd, GCancellable *cancellable, GError **error) { - gboolean success = FALSE; - guint8 *buf; gsize len; - - buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error); + g_autofree guint8 *buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error); if (!buf) - goto out; + return FALSE; if (!g_utf8_validate ((char*)buf, len, NULL)) { @@ -465,19 +427,12 @@ glnx_fd_readall_utf8 (int fd, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid UTF-8"); - goto out; + return FALSE; } - success = TRUE; - out: - if (success) - { - if (out_len) - *out_len = len; - return (char*)buf; - } - g_free (buf); - return NULL; + if (out_len) + *out_len = len; + return (char*)g_steal_pointer (&buf); } /** @@ -501,36 +456,20 @@ glnx_file_get_contents_utf8_at (int dfd, GCancellable *cancellable, GError **error) { - gboolean success = FALSE; - glnx_fd_close int fd = -1; - char *buf = NULL; - gsize len; - dfd = glnx_dirfd_canonicalize (dfd); - do - fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC); - while (G_UNLIKELY (fd == -1 && errno == EINTR)); + glnx_fd_close int fd = TEMP_FAILURE_RETRY (openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC)); if (G_UNLIKELY (fd == -1)) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_null_throw_errno_prefix (error, "open(%s)", subpath); - buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error); + gsize len; + g_autofree char *buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error); if (G_UNLIKELY(!buf)) - goto out; - - success = TRUE; - out: - if (success) - { - if (out_len) - *out_len = len; - return buf; - } - g_free (buf); - return NULL; + return FALSE; + + if (out_len) + *out_len = len; + return g_steal_pointer (&buf); } /** @@ -585,43 +524,32 @@ copy_symlink_at (int src_dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - g_autofree char *buf = NULL; - - buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error); + g_autofree char *buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error); if (!buf) - goto out; + return FALSE; if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } - + return glnx_throw_errno_prefix (error, "symlinkat"); + if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) { g_autoptr(GVariant) xattrs = NULL; if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs, cancellable, error)) - goto out; + return FALSE; if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs, cancellable, error)) - goto out; + return FALSE; } - + if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath, src_stbuf->st_uid, src_stbuf->st_gid, AT_SYMLINK_NOFOLLOW)) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + return glnx_throw_errno (error); - ret = TRUE; - out: - return ret; + return TRUE; } #define COPY_BUFFER_SIZE (16*1024) @@ -916,7 +844,6 @@ glnx_file_replace_contents_at (int dfd, return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len, (mode_t) -1, (uid_t) -1, (gid_t) -1, flags, cancellable, error); - } /** @@ -948,8 +875,6 @@ glnx_file_replace_contents_with_perms_at (int dfd, int r; char *dnbuf = strdupa (subpath); const char *dn = dirname (dnbuf); - g_autofree char *tmpfile_path = NULL; - glnx_fd_close int fd = -1; dfd = glnx_dirfd_canonicalize (dfd); @@ -959,6 +884,8 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (mode == (mode_t) -1) mode = 0644; + glnx_fd_close int fd = -1; + g_autofree char *tmpfile_path = NULL; if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC, &fd, &tmpfile_path, error)) @@ -974,16 +901,14 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (r != 0) { errno = r; - glnx_set_error_from_errno (error); - return FALSE; + return glnx_throw_errno_prefix (error, "fallocate"); } } if ((r = glnx_loop_write (fd, buf, len)) != 0) { errno = -r; - glnx_set_error_from_errno (error); - return FALSE; + return glnx_throw_errno (error); } if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) @@ -994,10 +919,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { if (errno != ENOENT) - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno (error); do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; } else @@ -1006,27 +928,18 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (do_sync) { if (fdatasync (fd) != 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno_prefix (error, "fdatasync"); } } if (uid != (uid_t) -1) { if (fchown (fd, uid, gid) != 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno (error); } if (fchmod (fd, mode) != 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } + return glnx_throw_errno (error); if (!glnx_link_tmpfile_at (dfd, GLNX_LINK_TMPFILE_REPLACE, fd, tmpfile_path, dfd, subpath, error)) @@ -1055,10 +968,7 @@ glnx_stream_fstat (GFileDescriptorBased *stream, int fd = g_file_descriptor_based_get_fd (stream); if (fstat (fd, stbuf) == -1) - { - glnx_set_prefix_error_from_errno (error, "%s", "fstat"); - return FALSE; - } + return glnx_throw_errno_prefix (error, "fstat"); return TRUE; } From 3a4d0f4684f4653338c4756c8a1abfc6b5738467 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 27 Apr 2017 13:51:31 -0400 Subject: [PATCH 114/280] fdio: Expose glnx_regfile_copy_bytes(), rewrite: GNU style, POSIX errno NOTE: This changes the error handling API of `glnx_loop_write()` to be "old school POSIX" instead of "systemd". In ostree in a few places we use `g_output_stream_splice()`. I thought this would use `splice()`, but actually it doesn't today. They also, if a cancellable is provided, end up dropping into `poll()` for every read and write. (In addition to copying data to/from userspace). My opinion on this is - for *local files* that's dumb. In the big picture, you really only need cancellation when copying gigabytes. Down the line, we could perhaps add a `glnx_copy_bytes_cancellable()` that only did that check e.g. every gigabyte of copied data. And when we do that we should use `g_cancellable_set_error_if_cancelled()` rather than a `poll()` with the regular file FD, since regular files are *always* readable and writable. For my use case with rpm-ostree though, we don't have gigabyte sized files, and seeing all of the `poll()` calls in strace is annoying. So let's have the non-cancellable file copying API that's modern and uses both reflink and `sendfile()` if available, in that order. My plan at some point once this is tested more is to migrate this code into GLib. Note that in order to keep our APIs consistent, I switched the systemd-imported code to "old school POSIX" error conventions. Otherwise we'd have *3* (POSIX, systemd, and GError) and particularly given the first two are easily confused, it'd be a recipe for bugs. --- glnx-fdio.c | 202 +++++++++++++++++++++++++++++----------------------- glnx-fdio.h | 3 + 2 files changed, 114 insertions(+), 91 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index cda627a0..df4c8518 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -554,117 +554,141 @@ copy_symlink_at (int src_dfd, #define COPY_BUFFER_SIZE (16*1024) -/* From systemd */ +/* Most of the code below is from systemd, but has been reindented to GNU style, + * and changed to use POSIX error conventions (return -1, set errno) to more + * conveniently fit in with the rest of libglnx. + */ static int btrfs_reflink(int infd, int outfd) { - int r; - g_return_val_if_fail(infd >= 0, -1); g_return_val_if_fail(outfd >= 0, -1); - r = ioctl(outfd, BTRFS_IOC_CLONE, infd); - if (r < 0) - return -errno; - - return 0; + return ioctl (outfd, BTRFS_IOC_CLONE, infd); } -int glnx_loop_write(int fd, const void *buf, size_t nbytes) { - const uint8_t *p = buf; - - g_return_val_if_fail(fd >= 0, -1); - g_return_val_if_fail(buf, -1); +/* Like write(), but loop until @nbytes are written, or an error + * occurs. + * + * On error, -1 is returned an @errno is set. NOTE: This is an + * API change from previous versions of this function. + */ +int +glnx_loop_write(int fd, const void *buf, size_t nbytes) +{ + const uint8_t *p = buf; - errno = 0; + g_return_val_if_fail(fd >= 0, -1); + g_return_val_if_fail(buf, -1); - while (nbytes > 0) { - ssize_t k; + errno = 0; - k = write(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; + while (nbytes > 0) + { + ssize_t k; - return -errno; - } + k = write(fd, p, nbytes); + if (k < 0) + { + if (errno == EINTR) + continue; - if (k == 0) /* Can't really happen */ - return -EIO; + return -1; + } - p += k; - nbytes -= k; + if (k == 0) /* Can't really happen */ + { + errno = EIO; + return -1; } - return 0; -} + p += k; + nbytes -= k; + } -static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { - bool try_sendfile = true; - int r; + return 0; +} - g_return_val_if_fail (fdf >= 0, -1); - g_return_val_if_fail (fdt >= 0, -1); +/* Read from @fdf until EOF, writing to @fdt. If @try_reflink is %TRUE, + * attempt to use any "reflink" functionality; see e.g. https://lwn.net/Articles/331808/ + * + * The file descriptor @fdf must refer to a regular file. + * + * If provided, @max_bytes specifies the maximum number of bytes to read from @fdf. + * On error, this function returns `-1` and @errno will be set. + */ +int +glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink) +{ + bool try_sendfile = true; + int r; - /* Try btrfs reflinks first. */ - if (try_reflink && max_bytes == (off_t) -1) { - r = btrfs_reflink(fdf, fdt); - if (r >= 0) - return r; - } + g_return_val_if_fail (fdf >= 0, -1); + g_return_val_if_fail (fdt >= 0, -1); + g_return_val_if_fail (max_bytes >= -1, -1); - for (;;) { - size_t m = COPY_BUFFER_SIZE; - ssize_t n; + /* Try btrfs reflinks first. */ + if (try_reflink && max_bytes == (off_t) -1) + { + r = btrfs_reflink(fdf, fdt); + if (r >= 0) + return 0; + /* Fall through */ + } - if (max_bytes != (off_t) -1) { + while (TRUE) + { + size_t m = COPY_BUFFER_SIZE; + ssize_t n; - if (max_bytes <= 0) - return -EFBIG; + if (max_bytes != (off_t) -1) + { + if ((off_t) m > max_bytes) + m = (size_t) max_bytes; + } - if ((off_t) m > max_bytes) - m = (size_t) max_bytes; - } + /* First try sendfile(), unless we already tried */ + if (try_sendfile) + { + n = sendfile (fdt, fdf, NULL, m); + if (n < 0) + { + if (errno != EINVAL && errno != ENOSYS) + return -1; - /* First try sendfile(), unless we already tried */ - if (try_sendfile) { - - n = sendfile(fdt, fdf, NULL, m); - if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) - return -errno; - - try_sendfile = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else if (n > 0) - /* Succcess! */ - goto next; - } + try_sendfile = false; + /* use fallback below */ + } + else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; + } - /* As a fallback just copy bits by hand */ - { - char buf[m]; + /* As a fallback just copy bits by hand */ + { char buf[m]; - n = read(fdf, buf, m); - if (n < 0) - return -errno; - if (n == 0) /* EOF */ - break; + n = read (fdf, buf, m); + if (n < 0) + return -1; + if (n == 0) /* EOF */ + break; - r = glnx_loop_write(fdt, buf, (size_t) n); - if (r < 0) - return r; - } + if (glnx_loop_write (fdt, buf, (size_t) n) < 0) + return -1; + } - next: - if (max_bytes != (off_t) -1) { - g_assert(max_bytes >= n); - max_bytes -= n; - } + next: + if (max_bytes != (off_t) -1) + { + g_assert(max_bytes >= n); + max_bytes -= n; + if (max_bytes == 0) + break; } + } - return 0; + return 0; } /** @@ -752,10 +776,9 @@ glnx_file_copy_at (int src_dfd, goto out; } - r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE); + r = glnx_regfile_copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE); if (r < 0) { - errno = -r; glnx_set_error_from_errno (error); goto out; } @@ -905,17 +928,14 @@ glnx_file_replace_contents_with_perms_at (int dfd, } } - if ((r = glnx_loop_write (fd, buf, len)) != 0) - { - errno = -r; - return glnx_throw_errno (error); - } - + if (glnx_loop_write (fd, buf, len) < 0) + return glnx_throw_errno (error); + if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) { struct stat stbuf; gboolean do_sync; - + if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { if (errno != ENOENT) diff --git a/glnx-fdio.h b/glnx-fdio.h index 56d0b78e..deff22b2 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -131,6 +131,9 @@ glnx_readlinkat_malloc (int dfd, int glnx_loop_write (int fd, const void *buf, size_t nbytes); +int +glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink); + typedef enum { GLNX_FILE_COPY_OVERWRITE = (1 << 0), GLNX_FILE_COPY_NOXATTRS = (1 << 1), From 32231fdb5273dd2a812c61549d6c0e681c0f5d59 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Thu, 11 May 2017 10:48:30 -0400 Subject: [PATCH 115/280] glnx-errors.h: add a glnx_throw_prefix() variant For completeness. It just looks much cleaner than doing the `, FALSE` trick. It also takes care of appending the ': ' for you like its errno version. --- glnx-errors.c | 24 ++++++++++++++++++------ glnx-errors.h | 37 ++++++++++++++++++++++++++++++++++--- tests/test-libglnx-errors.c | 7 +++++++ 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/glnx-errors.c b/glnx-errors.c index edbce205..48008733 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -23,6 +23,22 @@ #include #include +void +glnx_real_set_prefix_error_va (GError *error, + const char *format, + va_list args) +{ + if (error == NULL) + return; + + g_autofree char *old_msg = g_steal_pointer (&error->message); + g_autoptr(GString) buf = g_string_new (""); + g_string_append_vprintf (buf, format, args); + g_string_append (buf, ": "); + g_string_append (buf, old_msg); + error->message = g_string_free (g_steal_pointer (&buf), FALSE); +} + void glnx_real_set_prefix_error_from_errno_va (GError **error, gint errsv, @@ -32,13 +48,9 @@ glnx_real_set_prefix_error_from_errno_va (GError **error, if (!error) return; - /* TODO - enhance GError to have a "set and take ownership" API */ - g_autoptr(GString) buf = g_string_new (""); - g_string_append_vprintf (buf, format, args); - g_string_append (buf, ": "); - g_string_append (buf, g_strerror (errsv)); g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), - buf->str); + g_strerror (errsv)); + glnx_real_set_prefix_error_va (*error, format, args); } diff --git a/glnx-errors.h b/glnx-errors.h index 2f6b9628..e26a513e 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -30,7 +30,7 @@ G_BEGIN_DECLS * This function returns %FALSE so it can be used conveniently in a single * statement: * - * `` + * ``` * if (strcmp (foo, "somevalue") != 0) * return glnx_throw (error, "key must be somevalue, not '%s'", foo); * ``` @@ -49,16 +49,47 @@ glnx_throw (GError **error, const char *fmt, ...) return FALSE; } -/* Like glnx_throw(), but yields a NULL pointer. */ +/* Like `glnx_throw ()`, but returns %NULL. */ #define glnx_null_throw(error, args...) \ ({glnx_throw (error, args); NULL;}) +/* Implementation detail of glnx_throw_prefix() */ +void glnx_real_set_prefix_error_va (GError *error, + const char *format, + va_list args) G_GNUC_PRINTF (2,0); + +/* Prepend to @error's message by `$prefix: ` where `$prefix` is computed via + * printf @fmt. Returns %FALSE so it can be used conveniently in a single + * statement: + * + * ``` + * if (!function_that_fails (s, error)) + * return glnx_throw_prefix (error, "while handling '%s'", s); + * ``` + * */ +static inline gboolean G_GNUC_PRINTF (2,3) +glnx_prefix_error (GError **error, const char *fmt, ...) +{ + if (error == NULL) + return FALSE; + + va_list args; + va_start (args, fmt); + glnx_real_set_prefix_error_va (*error, fmt, args); + va_end (args); + return FALSE; +} + +/* Like `glnx_prefix_error ()`, but returns %NULL. */ +#define glnx_prefix_error_null(error, args...) \ + ({glnx_prefix_error (error, args); NULL;}) + /* Set @error using the value of `g_strerror (errno)`. * * This function returns %FALSE so it can be used conveniently in a single * statement: * - * `` + * ``` * if (unlinkat (fd, somepathname) < 0) * return glnx_throw_errno (error); * ``` diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 7950244f..721818ba 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -60,6 +60,9 @@ test_error_errno (void) { g_assert (!glnx_throw_errno (&error)); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (!glnx_prefix_error (&error, "myprefix")); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, "myprefix: ")); g_clear_error (&error); } else @@ -71,6 +74,10 @@ test_error_errno (void) gpointer dummy = glnx_null_throw_errno (&error); g_assert (dummy == NULL); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + dummy = glnx_prefix_error_null (&error, "myprefix"); + g_assert (dummy == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_assert (g_str_has_prefix (error->message, "myprefix: ")); g_clear_error (&error); } else From 9929adcd99d15dc4f5a925f9b7ec2318f646f1bf Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 15 May 2017 16:07:32 -0400 Subject: [PATCH 116/280] fdio: Redo tmpfile API with GLnxTmpfile struct The core problem with the previous tmpfile code is we don't have an autocleanup that calls `unlinkat` in the non-`O_TMPFILE` case. And even if we did, it'd be awkward still since the `glnx_link_tmpfile_at()` call *consumes* the tmpfile. Fix this by introducing a struct with a cleanup macro. This simplifies a number of the callers in libostree - a notable case is where we had two arrays, one of fds, one of paths. It makes other places in libostree a bit more complex, but that's because some of the commit code paths want to deal with temporary *symlinks* too. Most callers are better though - in libglnx itself, `glnx_file_copy_at()` now correctly unlinks on failure for example. --- glnx-fdio.c | 96 +++++++++++++++++++++++++++++++---------------------- glnx-fdio.h | 16 ++++++--- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index df4c8518..b8ed0a59 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -146,12 +146,37 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, return 0; } +/* Deallocate a tmpfile */ +void +glnx_tmpfile_clear (GLnxTmpfile *tmpf) +{ + if (tmpf->src_dfd == -1) + return; + if (tmpf->fd == -1) + return; + (void) close (tmpf->fd); + /* If ->path is set, we're likely aborting due to an error. Clean it up */ + if (tmpf->path) + { + (void) unlinkat (tmpf->src_dfd, tmpf->path, 0); + g_free (tmpf->path); + } +} + +/* Allocate a temporary file, using Linux O_TMPFILE if available. + * The result will be stored in @out_tmpf, which is caller allocated + * so you can store it on the stack in common scenarios. + * + * Note that with O_TMPFILE, the file mode will be `000`; you likely + * want to chmod it before calling glnx_link_tmpfile_at(). + * + * The directory fd @dfd must live at least as long as the output @out_tmpf. + */ gboolean glnx_open_tmpfile_linkable_at (int dfd, const char *subpath, int flags, - int *out_fd, - char **out_path, + GLnxTmpfile *out_tmpf, GError **error) { glnx_fd_close int fd = -1; @@ -172,9 +197,9 @@ glnx_open_tmpfile_linkable_at (int dfd, return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); if (fd != -1) { - *out_fd = fd; - fd = -1; - *out_path = NULL; + out_tmpf->src_dfd = dfd; + out_tmpf->fd = fd; fd = -1; /* Transfer */ + out_tmpf->path = NULL; return TRUE; } /* Fallthrough */ @@ -182,7 +207,7 @@ glnx_open_tmpfile_linkable_at (int dfd, { g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL); const guint count_max = 100; - + for (count = 0; count < count_max; count++) { glnx_gen_temp_name (tmp); @@ -197,9 +222,9 @@ glnx_open_tmpfile_linkable_at (int dfd, } else { - *out_fd = fd; - fd = -1; - *out_path = g_steal_pointer (&tmp); + out_tmpf->src_dfd = dfd; + out_tmpf->fd = fd; fd = -1; /* Transfer */ + out_tmpf->path = g_steal_pointer (&tmp); return TRUE; } } @@ -209,11 +234,12 @@ glnx_open_tmpfile_linkable_at (int dfd, return FALSE; } +/* Use this after calling glnx_open_tmpfile_linkable_at() to give + * the file its final name (link into place). + */ gboolean -glnx_link_tmpfile_at (int dfd, +glnx_link_tmpfile_at (GLnxTmpfile *tmpf, GLnxLinkTmpfileReplaceMode mode, - int fd, - const char *tmpfile_path, int target_dfd, const char *target, GError **error) @@ -221,46 +247,40 @@ glnx_link_tmpfile_at (int dfd, const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE); const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST); - g_return_val_if_fail (fd >= 0, FALSE); + g_return_val_if_fail (tmpf->src_dfd >= 0, FALSE); /* Unlike the original systemd code, this function also supports * replacing existing files. */ /* We have `tmpfile_path` for old systems without O_TMPFILE. */ - if (tmpfile_path) + if (tmpf->path) { if (replace) { /* We have a regular tempfile, we're overwriting - this is a * simple renameat(). */ - if (renameat (dfd, tmpfile_path, target_dfd, target) < 0) - { - int errsv = errno; - (void) unlinkat (dfd, tmpfile_path, 0); - errno = errsv; - return glnx_throw_errno_prefix (error, "renameat"); - } + if (renameat (tmpf->src_dfd, tmpf->path, target_dfd, target) < 0) + return glnx_throw_errno_prefix (error, "renameat"); } else { /* We need to use renameat2(..., NOREPLACE) or emulate it */ - if (!rename_file_noreplace_at (dfd, tmpfile_path, target_dfd, target, + if (!rename_file_noreplace_at (tmpf->src_dfd, tmpf->path, target_dfd, target, ignore_eexist, error)) - { - (void) unlinkat (dfd, tmpfile_path, 0); - return FALSE; - } + return FALSE; } + /* Now, clear the pointer so we don't try to unlink it */ + g_clear_pointer (&tmpf->path, g_free); } else { /* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */ - char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(tmpf->fd) + 1]; - sprintf (proc_fd_path, "/proc/self/fd/%i", fd); + sprintf (proc_fd_path, "/proc/self/fd/%i", tmpf->fd); if (replace) { @@ -907,11 +927,9 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (mode == (mode_t) -1) mode = 0644; - glnx_fd_close int fd = -1; - g_autofree char *tmpfile_path = NULL; + g_auto(GLnxTmpfile) tmpf = GLNX_TMPFILE_INIT; if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC, - &fd, &tmpfile_path, - error)) + &tmpf, error)) return FALSE; if (len == -1) @@ -920,7 +938,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, /* Note that posix_fallocate does *not* set errno but returns it. */ if (len > 0) { - r = posix_fallocate (fd, 0, len); + r = posix_fallocate (tmpf.fd, 0, len); if (r != 0) { errno = r; @@ -928,7 +946,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, } } - if (glnx_loop_write (fd, buf, len) < 0) + if (glnx_loop_write (tmpf.fd, buf, len) < 0) return glnx_throw_errno (error); if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) @@ -947,22 +965,22 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (do_sync) { - if (fdatasync (fd) != 0) + if (fdatasync (tmpf.fd) != 0) return glnx_throw_errno_prefix (error, "fdatasync"); } } if (uid != (uid_t) -1) { - if (fchown (fd, uid, gid) != 0) + if (fchown (tmpf.fd, uid, gid) != 0) return glnx_throw_errno (error); } - if (fchmod (fd, mode) != 0) + if (fchmod (tmpf.fd, mode) != 0) return glnx_throw_errno (error); - if (!glnx_link_tmpfile_at (dfd, GLNX_LINK_TMPFILE_REPLACE, - fd, tmpfile_path, dfd, subpath, error)) + if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE, + dfd, subpath, error)) return FALSE; return TRUE; diff --git a/glnx-fdio.h b/glnx-fdio.h index deff22b2..cc36ca48 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -48,12 +48,20 @@ const char *glnx_basename (const char *path) return (basename) (path); } +typedef struct { + int src_dfd; + int fd; + char *path; +} GLnxTmpfile; +#define GLNX_TMPFILE_INIT { .src_dfd = -1 }; +void glnx_tmpfile_clear (GLnxTmpfile *tmpf); +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear); + gboolean glnx_open_tmpfile_linkable_at (int dfd, const char *subpath, int flags, - int *out_fd, - char **out_path, + GLnxTmpfile *out_tmpf, GError **error); typedef enum { @@ -63,10 +71,8 @@ typedef enum { } GLnxLinkTmpfileReplaceMode; gboolean -glnx_link_tmpfile_at (int dfd, +glnx_link_tmpfile_at (GLnxTmpfile *tmpf, GLnxLinkTmpfileReplaceMode flags, - int fd, - const char *tmpfile_path, int target_dfd, const char *target, GError **error); From 4fbd48fb88906b3eaa9de31c6c798c4f15ee05a4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 17 May 2017 16:57:54 -0400 Subject: [PATCH 117/280] fdio: Add missing return in tmpfile error case Just noticed this while reading the code. --- glnx-fdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index b8ed0a59..ac62a5ee 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -314,6 +314,7 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, { g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, "Exhausted %u attempts to create temporary file", count); + return FALSE; } if (renameat (target_dfd, tmpname_buf, target_dfd, target) < 0) { From 2f8fdf80ec60aff343f4a6b23d1549b28d43dd0c Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 19 May 2017 11:26:55 +0200 Subject: [PATCH 118/280] fdio: Allow using AT_FDCWD with GlnxTmpfile Add an `initialized` member which means we work by default in structs allocated with `g_new0` etc. and don't need a special initializer. This also fixes a bug where we need to support `src_dfd == -1` or `AT_FDCWD`. This fixes flatpak which uses AT_FDCWD. Modified-by: Colin Walters --- glnx-fdio.c | 18 +++++++++++------- glnx-fdio.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index ac62a5ee..19de9ec2 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -150,7 +150,7 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, void glnx_tmpfile_clear (GLnxTmpfile *tmpf) { - if (tmpf->src_dfd == -1) + if (!tmpf->initialized) return; if (tmpf->fd == -1) return; @@ -182,6 +182,8 @@ glnx_open_tmpfile_linkable_at (int dfd, glnx_fd_close int fd = -1; int count; + dfd = glnx_dirfd_canonicalize (dfd); + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE); @@ -197,8 +199,9 @@ glnx_open_tmpfile_linkable_at (int dfd, return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); if (fd != -1) { - out_tmpf->src_dfd = dfd; - out_tmpf->fd = fd; fd = -1; /* Transfer */ + out_tmpf->initialized = TRUE; + out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ + out_tmpf->fd = glnx_steal_fd (&fd); out_tmpf->path = NULL; return TRUE; } @@ -222,8 +225,9 @@ glnx_open_tmpfile_linkable_at (int dfd, } else { - out_tmpf->src_dfd = dfd; - out_tmpf->fd = fd; fd = -1; /* Transfer */ + out_tmpf->initialized = TRUE; + out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ + out_tmpf->fd = glnx_steal_fd (&fd); out_tmpf->path = g_steal_pointer (&tmp); return TRUE; } @@ -247,7 +251,7 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE); const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST); - g_return_val_if_fail (tmpf->src_dfd >= 0, FALSE); + g_return_val_if_fail (tmpf->fd >= 0, FALSE); /* Unlike the original systemd code, this function also supports * replacing existing files. @@ -928,7 +932,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (mode == (mode_t) -1) mode = 0644; - g_auto(GLnxTmpfile) tmpf = GLNX_TMPFILE_INIT; + g_auto(GLnxTmpfile) tmpf = { 0, }; if (!glnx_open_tmpfile_linkable_at (dfd, dn, O_WRONLY | O_CLOEXEC, &tmpf, error)) return FALSE; diff --git a/glnx-fdio.h b/glnx-fdio.h index cc36ca48..dda32d1a 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -49,11 +49,11 @@ const char *glnx_basename (const char *path) } typedef struct { + gboolean initialized; int src_dfd; int fd; char *path; } GLnxTmpfile; -#define GLNX_TMPFILE_INIT { .src_dfd = -1 }; void glnx_tmpfile_clear (GLnxTmpfile *tmpf); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear); From 66d162873c6f23d4628af80aa1f46fdf2d9e5e07 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 26 May 2017 16:01:29 -0400 Subject: [PATCH 119/280] dirfd,xattrs: Port mostly to new code style Not everything, but a good chunk of the remaining bits. --- glnx-dirfd.c | 39 ++++----------- glnx-xattrs.c | 129 +++++++++++++------------------------------------- 2 files changed, 42 insertions(+), 126 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 12b983c1..388f2a32 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -99,19 +99,14 @@ glnx_dirfd_iterator_init_at (int dfd, GLnxDirFdIterator *out_dfd_iter, GError **error) { - gboolean ret = FALSE; glnx_fd_close int fd = -1; - if (!glnx_opendirat (dfd, path, follow, &fd, error)) - goto out; + return FALSE; - if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error)) - goto out; - fd = -1; /* Transfer ownership */ + if (!glnx_dirfd_iterator_init_take_fd (glnx_steal_fd (&fd), out_dfd_iter, error)) + return FALSE; - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -128,24 +123,16 @@ glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error) { - gboolean ret = FALSE; GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; - DIR *d = NULL; - - d = fdopendir (dfd); + DIR *d = fdopendir (dfd); if (!d) - { - glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); - goto out; - } + return glnx_throw_errno_prefix (error, "fdopendir"); real_dfd_iter->fd = dfd; real_dfd_iter->d = d; real_dfd_iter->initialized = TRUE; - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -165,31 +152,25 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; g_return_val_if_fail (out_dent, FALSE); g_return_val_if_fail (dfd_iter->initialized, FALSE); if (g_cancellable_set_error_if_cancelled (cancellable, error)) - goto out; + return FALSE; do { errno = 0; *out_dent = readdir (real_dfd_iter->d); if (*out_dent == NULL && errno != 0) - { - glnx_set_prefix_error_from_errno (error, "%s", "fdopendir"); - goto out; - } + return glnx_throw_errno_prefix (error, "readdir"); } while (*out_dent && (strcmp ((*out_dent)->d_name, ".") == 0 || strcmp ((*out_dent)->d_name, "..") == 0)); - ret = TRUE; - out: - return ret; + return TRUE; } /** diff --git a/glnx-xattrs.c b/glnx-xattrs.c index eadb6b15..3c1a9c59 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -270,31 +270,22 @@ set_all_xattrs_for_path (const char *path, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - int i, n; - - n = g_variant_n_children (xattrs); - for (i = 0; i < n; i++) + const guint n = g_variant_n_children (xattrs); + for (guint i = 0; i < n; i++) { const guint8* name; g_autoptr(GVariant) value = NULL; - const guint8* value_data; - gsize value_len; - g_variant_get_child (xattrs, i, "(^&ay@ay)", &name, &value); - value_data = g_variant_get_fixed_array (value, &value_len, 1); - + + gsize value_len; + const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1); + if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0) - { - glnx_set_prefix_error_from_errno (error, "%s", "lsetxattr"); - goto out; - } + return glnx_throw_errno_prefix (error, "lsetxattr"); } - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -347,35 +338,22 @@ glnx_fd_set_all_xattrs (int fd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - int i, n; - - n = g_variant_n_children (xattrs); - for (i = 0; i < n; i++) + const guint n = g_variant_n_children (xattrs); + for (guint i = 0; i < n; i++) { const guint8* name; - const guint8* value_data; g_autoptr(GVariant) value = NULL; - gsize value_len; - int res; - g_variant_get_child (xattrs, i, "(^&ay@ay)", &name, &value); - value_data = g_variant_get_fixed_array (value, &value_len, 1); - - do - res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0); - while (G_UNLIKELY (res == -1 && errno == EINTR)); - if (G_UNLIKELY (res == -1)) - { - glnx_set_prefix_error_from_errno (error, "%s", "fsetxattr"); - goto out; - } + + gsize value_len; + const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1); + + if (TEMP_FAILURE_RETRY (fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0)) < 0) + return glnx_throw_errno_prefix (error, "fsetxattr"); } - ret = TRUE; - out: - return ret; + return TRUE; } /** @@ -395,35 +373,17 @@ glnx_lgetxattrat (int dfd, GError **error) { char pathbuf[PATH_MAX]; - GBytes *bytes = NULL; - ssize_t bytes_read, real_size; - guint8 *buf; - snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); - do - bytes_read = lgetxattr (pathbuf, attribute, NULL, 0); - while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); - if (G_UNLIKELY (bytes_read < 0)) - { - glnx_set_error_from_errno (error); - goto out; - } + ssize_t bytes_read, real_size; + if (TEMP_FAILURE_RETRY (bytes_read = lgetxattr (pathbuf, attribute, NULL, 0)) < 0) + return glnx_null_throw_errno_prefix (error, "lgetxattr"); - buf = g_malloc (bytes_read); - do - real_size = lgetxattr (pathbuf, attribute, buf, bytes_read); - while (G_UNLIKELY (real_size < 0 && errno == EINTR)); - if (G_UNLIKELY (real_size < 0)) - { - glnx_set_error_from_errno (error); - g_free (buf); - goto out; - } + g_autofree guint8 *buf = g_malloc (bytes_read); + if (TEMP_FAILURE_RETRY (real_size = lgetxattr (pathbuf, attribute, buf, bytes_read)) < 0) + return glnx_null_throw_errno_prefix (error, "lgetxattr"); - bytes = g_bytes_new_take (buf, real_size); - out: - return bytes; + return g_bytes_new_take (g_steal_pointer (&buf), real_size); } /** @@ -439,33 +399,16 @@ glnx_fgetxattr_bytes (int fd, const char *attribute, GError **error) { - GBytes *bytes = NULL; ssize_t bytes_read, real_size; - guint8 *buf; - do - bytes_read = fgetxattr (fd, attribute, NULL, 0); - while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); - if (G_UNLIKELY (bytes_read < 0)) - { - glnx_set_error_from_errno (error); - goto out; - } + if (TEMP_FAILURE_RETRY (bytes_read = fgetxattr (fd, attribute, NULL, 0)) < 0) + return glnx_null_throw_errno_prefix (error, "fgetxattr"); - buf = g_malloc (bytes_read); - do - real_size = fgetxattr (fd, attribute, buf, bytes_read); - while (G_UNLIKELY (real_size < 0 && errno == EINTR)); - if (G_UNLIKELY (real_size < 0)) - { - glnx_set_error_from_errno (error); - g_free (buf); - goto out; - } + g_autofree guint8 *buf = g_malloc (bytes_read); + if (TEMP_FAILURE_RETRY (real_size = fgetxattr (fd, attribute, buf, bytes_read)) < 0) + return glnx_null_throw_errno_prefix (error, "fgetxattr"); - bytes = g_bytes_new_take (buf, real_size); - out: - return bytes; + return g_bytes_new_take (g_steal_pointer (&buf), real_size); } /** @@ -490,18 +433,10 @@ glnx_lsetxattrat (int dfd, GError **error) { char pathbuf[PATH_MAX]; - int res; - snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); - do - res = lsetxattr (subpath, attribute, value, len, flags); - while (G_UNLIKELY (res == -1 && errno == EINTR)); - if (G_UNLIKELY (res == -1)) - { - glnx_set_error_from_errno (error); - return FALSE; - } + if (TEMP_FAILURE_RETRY (lsetxattr (subpath, attribute, value, len, flags)) < 0) + return glnx_throw_errno_prefix (error, "lsetxattr"); return TRUE; } From f5ba01cf65309164adb06067b39cf28c071e1ccb Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 31 May 2017 14:48:53 -0400 Subject: [PATCH 120/280] dirfd: Have dfd iter _take_fd() take a pointer and do a steal This avoids callers having to use `glnx_steal_fd()` on their own; in general, I think we should implement move semantics like this at the callee level. Another reason to do this is there's a subtle problem with doing: ``` somefunction (steal_value (&v), ..., error); ``` in that if `somefunction` throws, it may not have taken ownership of the value. At least `glnx_dirfd_iterator_init_take_fd()` didn't. --- glnx-dirfd.c | 10 +++++----- glnx-dirfd.h | 2 +- glnx-shutil.c | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 388f2a32..bb73b015 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -103,7 +103,7 @@ glnx_dirfd_iterator_init_at (int dfd, if (!glnx_opendirat (dfd, path, follow, &fd, error)) return FALSE; - if (!glnx_dirfd_iterator_init_take_fd (glnx_steal_fd (&fd), out_dfd_iter, error)) + if (!glnx_dirfd_iterator_init_take_fd (&fd, out_dfd_iter, error)) return FALSE; return TRUE; @@ -111,7 +111,7 @@ glnx_dirfd_iterator_init_at (int dfd, /** * glnx_dirfd_iterator_init_take_fd: - * @dfd: File descriptor - ownership is taken + * @dfd: File descriptor - ownership is taken, and the value is set to -1 * @dfd_iter: A directory iterator * @error: Error * @@ -119,16 +119,16 @@ glnx_dirfd_iterator_init_at (int dfd, * iteration. */ gboolean -glnx_dirfd_iterator_init_take_fd (int dfd, +glnx_dirfd_iterator_init_take_fd (int *dfd, GLnxDirFdIterator *dfd_iter, GError **error) { GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; - DIR *d = fdopendir (dfd); + DIR *d = fdopendir (*dfd); if (!d) return glnx_throw_errno_prefix (error, "fdopendir"); - real_dfd_iter->fd = dfd; + real_dfd_iter->fd = glnx_steal_fd (dfd); real_dfd_iter->d = d; real_dfd_iter->initialized = TRUE; diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 0cb79e69..581b3032 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -55,7 +55,7 @@ typedef struct GLnxDirFdIterator GLnxDirFdIterator; gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path, gboolean follow, GLnxDirFdIterator *dfd_iter, GError **error); -gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error); +gboolean glnx_dirfd_iterator_init_take_fd (int *dfd, GLnxDirFdIterator *dfd_iter, GError **error); gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, struct dirent **out_dent, GCancellable *cancellable, diff --git a/glnx-shutil.c b/glnx-shutil.c index 6a1cdd4a..2e02eea4 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -110,9 +110,8 @@ glnx_shutil_rm_rf_at (int dfd, } else { - if (!glnx_dirfd_iterator_init_take_fd (target_dfd, &dfd_iter, error)) + if (!glnx_dirfd_iterator_init_take_fd (&target_dfd, &dfd_iter, error)) return FALSE; - target_dfd = -1; if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error)) return FALSE; From 05abf2143f967cd85b1b1b777ee412a581979cf1 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 12 Jun 2017 17:00:53 -0400 Subject: [PATCH 121/280] fdio: Add glnx_try_fallocate() The glibc `posix_fallocate()` implementation has a bad fallback, and further we need to handle `EOPNOTSUPP` for musl. https://github.com/flatpak/flatpak/issues/802 --- glnx-fdio.c | 13 ++----------- glnx-fdio.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 19de9ec2..87e2dbb9 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -920,7 +920,6 @@ glnx_file_replace_contents_with_perms_at (int dfd, GCancellable *cancellable, GError **error) { - int r; char *dnbuf = strdupa (subpath); const char *dn = dirname (dnbuf); @@ -940,16 +939,8 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (len == -1) len = strlen ((char*)buf); - /* Note that posix_fallocate does *not* set errno but returns it. */ - if (len > 0) - { - r = posix_fallocate (tmpf.fd, 0, len); - if (r != 0) - { - errno = r; - return glnx_throw_errno_prefix (error, "fallocate"); - } - } + if (!glnx_try_fallocate (tmpf.fd, 0, len, error)) + return FALSE; if (glnx_loop_write (tmpf.fd, buf, len) < 0) return glnx_throw_errno (error); diff --git a/glnx-fdio.h b/glnx-fdio.h index dda32d1a..e52c8237 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -166,6 +166,38 @@ int glnx_renameat2_noreplace (int olddirfd, const char *oldpath, int glnx_renameat2_exchange (int olddirfd, const char *oldpath, int newdirfd, const char *newpath); +/** + * glnx_try_fallocate: + * @fd: File descriptor + * @size: Size + * @error: Error + * + * Wrapper for Linux fallocate(). Explicitly ignores a @size of zero. + * Also, will silently do nothing if the underlying filesystem doesn't + * support it. Use this instead of posix_fallocate(), since the glibc fallback + * is bad: https://sourceware.org/bugzilla/show_bug.cgi?id=18515 + */ +static inline gboolean +glnx_try_fallocate (int fd, + off_t offset, + off_t size, + GError **error) +{ + /* This is just nicer than throwing an error */ + if (size == 0) + return TRUE; + + if (fallocate (fd, 0, offset, size) < 0) + { + if (errno == ENOSYS || errno == EOPNOTSUPP) + ; /* Ignore */ + else + return glnx_throw_errno_prefix (error, "fallocate"); + } + + return TRUE; +} + /** * glnx_fstat: * @fd: FD to stat From 9a1b77ef96b4eb9a916a71e7a04055c4c5665dea Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 13 Jun 2017 16:22:09 -0400 Subject: [PATCH 122/280] Add G_IN_SET, patch our internal users via spatch I originally tried to get this into GLib: https://bugzilla.gnome.org/show_bug.cgi?id=783751 But that looks like it's going to fail due to MSVC. Let's add it here at least so I can start using it tomorrow and not wait for the MSVC team to catch up. I renamed `glnx-alloca.h` to `glnx-macros.h` as a more natural collective home for things from systemd's `macro.h`. Finally, I used a Coccinelle spatch similar to the one referenced in the above BZ to patch our uses. --- Makefile-libglnx.am | 8 ++- glnx-alloca.h | 47 --------------- glnx-fdio.c | 7 +-- glnx-fdio.h | 3 +- glnx-macros.h | 115 ++++++++++++++++++++++++++++++++++++ glnx-xattrs.c | 5 +- libglnx.h | 2 +- tests/test-libglnx-macros.c | 48 +++++++++++++++ 8 files changed, 178 insertions(+), 57 deletions(-) delete mode 100644 glnx-alloca.h create mode 100644 glnx-macros.h create mode 100644 tests/test-libglnx-macros.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 805a9d04..6df05ea1 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -22,7 +22,7 @@ EXTRA_DIST += \ $(NULL) libglnx_la_SOURCES = \ - $(libglnx_srcpath)/glnx-alloca.h \ + $(libglnx_srcpath)/glnx-macros.h \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ $(libglnx_srcpath)/glnx-backports.h \ @@ -52,7 +52,7 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) -libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors +libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros TESTS += $(libglnx_tests) check_PROGRAMS += $(libglnx_tests) @@ -67,3 +67,7 @@ test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la test_libglnx_errors_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-errors.c test_libglnx_errors_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la + +test_libglnx_macros_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-macros.c +test_libglnx_macros_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +test_libglnx_macros_LDADD = $(libglnx_libs) libglnx.la diff --git a/glnx-alloca.h b/glnx-alloca.h deleted file mode 100644 index b0bf6a65..00000000 --- a/glnx-alloca.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2014,2015 Colin Walters . - * - * 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 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#pragma once - -#include -#include - -G_BEGIN_DECLS - -/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h - * at revision v228-666-gcf6c8c4 - */ -#define glnx_strjoina(a, ...) \ - ({ \ - const char *_appendees_[] = { a, __VA_ARGS__ }; \ - char *_d_, *_p_; \ - int _len_ = 0; \ - unsigned _i_; \ - for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ - _len_ += strlen(_appendees_[_i_]); \ - _p_ = _d_ = alloca(_len_ + 1); \ - for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ - _p_ = stpcpy(_p_, _appendees_[_i_]); \ - *_p_ = 0; \ - _d_; \ - }) - - -G_END_DECLS diff --git a/glnx-fdio.c b/glnx-fdio.c index 87e2dbb9..e496828e 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -65,7 +64,7 @@ glnx_renameat2_noreplace (int olddirfd, const char *oldpath, #ifndef ENABLE_WRPSEUDO_COMPAT if (renameat2 (olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) < 0) { - if (errno == EINVAL || errno == ENOSYS) + if (G_IN_SET(errno, EINVAL, ENOSYS)) { /* Fall through */ } @@ -119,7 +118,7 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, return 0; else { - if (errno == ENOSYS || errno == EINVAL) + if (G_IN_SET(errno, ENOSYS, EINVAL)) { /* Fall through */ } @@ -195,7 +194,7 @@ glnx_open_tmpfile_linkable_at (int dfd, * in full. */ #if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT) fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); - if (fd == -1 && !(errno == ENOSYS || errno == EISDIR || errno == EOPNOTSUPP)) + if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP))) return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); if (fd != -1) { diff --git a/glnx-fdio.h b/glnx-fdio.h index e52c8237..95574bd8 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -35,6 +35,7 @@ #include #undef basename +#include #include G_BEGIN_DECLS @@ -189,7 +190,7 @@ glnx_try_fallocate (int fd, if (fallocate (fd, 0, offset, size) < 0) { - if (errno == ENOSYS || errno == EOPNOTSUPP) + if (G_IN_SET(errno, ENOSYS, EOPNOTSUPP)) ; /* Ignore */ else return glnx_throw_errno_prefix (error, "fallocate"); diff --git a/glnx-macros.h b/glnx-macros.h new file mode 100644 index 00000000..87c25cad --- /dev/null +++ b/glnx-macros.h @@ -0,0 +1,115 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Colin Walters + * With original source from systemd: + * Copyright 2010 Lennart Poettering + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h + * at revision v228-666-gcf6c8c4 + */ +#define glnx_strjoina(a, ...) \ + ({ \ + const char *_appendees_[] = { a, __VA_ARGS__ }; \ + char *_d_, *_p_; \ + int _len_ = 0; \ + unsigned _i_; \ + for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ + _len_ += strlen(_appendees_[_i_]); \ + _p_ = _d_ = alloca(_len_ + 1); \ + for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ + _p_ = stpcpy(_p_, _appendees_[_i_]); \ + *_p_ = 0; \ + _d_; \ + }) + +#ifndef G_IN_SET + +/* Infrastructure for `G_IN_SET`; this code is copied from + * systemd's macro.h - please treat that version as canonical + * and submit patches first to systemd. + */ +#define _G_INSET_CASE_F(X) case X: +#define _G_INSET_CASE_F_1(CASE, X) _G_INSET_CASE_F(X) +#define _G_INSET_CASE_F_2(CASE, X, ...) CASE(X) _G_INSET_CASE_F_1(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_3(CASE, X, ...) CASE(X) _G_INSET_CASE_F_2(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_4(CASE, X, ...) CASE(X) _G_INSET_CASE_F_3(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_5(CASE, X, ...) CASE(X) _G_INSET_CASE_F_4(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_6(CASE, X, ...) CASE(X) _G_INSET_CASE_F_5(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_7(CASE, X, ...) CASE(X) _G_INSET_CASE_F_6(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_8(CASE, X, ...) CASE(X) _G_INSET_CASE_F_7(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_9(CASE, X, ...) CASE(X) _G_INSET_CASE_F_8(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_10(CASE, X, ...) CASE(X) _G_INSET_CASE_F_9(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_11(CASE, X, ...) CASE(X) _G_INSET_CASE_F_10(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_12(CASE, X, ...) CASE(X) _G_INSET_CASE_F_11(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_13(CASE, X, ...) CASE(X) _G_INSET_CASE_F_12(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_14(CASE, X, ...) CASE(X) _G_INSET_CASE_F_13(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_15(CASE, X, ...) CASE(X) _G_INSET_CASE_F_14(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_16(CASE, X, ...) CASE(X) _G_INSET_CASE_F_15(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_17(CASE, X, ...) CASE(X) _G_INSET_CASE_F_16(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_18(CASE, X, ...) CASE(X) _G_INSET_CASE_F_17(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_19(CASE, X, ...) CASE(X) _G_INSET_CASE_F_18(CASE, __VA_ARGS__) +#define _G_INSET_CASE_F_20(CASE, X, ...) CASE(X) _G_INSET_CASE_F_19(CASE, __VA_ARGS__) + +#define _G_INSET_GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME +#define _G_INSET_FOR_EACH_MAKE_CASE(...) \ + _G_INSET_GET_CASE_F(__VA_ARGS__,_G_INSET_CASE_F_20,_G_INSET_CASE_F_19,_G_INSET_CASE_F_18,_G_INSET_CASE_F_17,_G_INSET_CASE_F_16,_G_INSET_CASE_F_15,_G_INSET_CASE_F_14,_G_INSET_CASE_F_13,_G_INSET_CASE_F_12,_G_INSET_CASE_F_11, \ + _G_INSET_CASE_F_10,_G_INSET_CASE_F_9,_G_INSET_CASE_F_8,_G_INSET_CASE_F_7,_G_INSET_CASE_F_6,_G_INSET_CASE_F_5,_G_INSET_CASE_F_4,_G_INSET_CASE_F_3,_G_INSET_CASE_F_2,_G_INSET_CASE_F_1) \ + (_G_INSET_CASE_F,__VA_ARGS__) + +/* Note: claiming the name here even though it isn't upstream yet + * https://bugzilla.gnome.org/show_bug.cgi?id=783751 + */ +/** + * G_IN_SET: + * @x: Integer (or smaller) sized value + * @...: Elements to compare + * + * It's quite common to test whether or not `char` values or Unix @errno (among) others + * are members of a small set. Normally one has to choose to either use `if (x == val || x == otherval ...)` + * or a `switch` statement. This macro is useful to reduce duplication in the first case, + * where one can write simply `if (G_IN_SET (x, val, otherval))`, and avoid the verbosity + * that the `switch` statement requires. + */ +#define G_IN_SET(x, ...) \ + ({ \ + gboolean _g_inset_found = FALSE; \ + /* If the build breaks in the line below, you need to extend the case macros */ \ + static G_GNUC_UNUSED char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ + switch(x) { \ + _G_INSET_FOR_EACH_MAKE_CASE(__VA_ARGS__) \ + _g_inset_found = TRUE; \ + break; \ + default: \ + break; \ + } \ + _g_inset_found; \ + }) + +#endif /* ifndef G_IN_SET */ + + +G_END_DECLS diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 3c1a9c59..3ade89c1 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -249,7 +250,7 @@ glnx_dfd_name_get_all_xattrs (int dfd, GCancellable *cancellable, GError **error) { - if (dfd == AT_FDCWD || dfd == -1) + if (G_IN_SET(dfd, AT_FDCWD, -1)) { return get_xattrs_impl (name, -1, out_xattrs, cancellable, error); } @@ -306,7 +307,7 @@ glnx_dfd_name_set_all_xattrs (int dfd, GCancellable *cancellable, GError **error) { - if (dfd == AT_FDCWD || dfd == -1) + if (G_IN_SET(dfd, AT_FDCWD, -1)) { return set_all_xattrs_for_path (name, xattrs, cancellable, error); } diff --git a/libglnx.h b/libglnx.h index a5b23d07..494810d4 100644 --- a/libglnx.h +++ b/libglnx.h @@ -24,7 +24,7 @@ G_BEGIN_DECLS -#include +#include #include #include #include diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c new file mode 100644 index 00000000..d16c1006 --- /dev/null +++ b/tests/test-libglnx-macros.c @@ -0,0 +1,48 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Red Hat, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx.h" +#include +#include +#include +#include + +static void +test_inset (void) +{ + g_assert (G_IN_SET (7, 7)); + g_assert (G_IN_SET (7, 42, 7)); + g_assert (G_IN_SET (7, 7,42,3,9)); + g_assert (G_IN_SET (42, 7,42,3,9)); + g_assert (G_IN_SET (3, 7,42,3,9)); + g_assert (G_IN_SET (9, 7,42,3,9)); + g_assert (!G_IN_SET (8, 7,42,3,9)); + g_assert (!G_IN_SET (-1, 7,42,3,9)); + g_assert (G_IN_SET ('x', 'a', 'x', 'c')); + g_assert (!G_IN_SET ('y', 'a', 'x', 'c')) +} + +int main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/inset", test_inset); + return g_test_run(); +} From e8b7d8f60ca4918aee61abe22dea959d77f4f189 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Sat, 17 Jun 2017 14:52:26 -0400 Subject: [PATCH 123/280] test-libglnx-macros.c: fix missing semicolon --- tests/test-libglnx-macros.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index d16c1006..d45c5cf8 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -37,7 +37,7 @@ test_inset (void) g_assert (!G_IN_SET (8, 7,42,3,9)); g_assert (!G_IN_SET (-1, 7,42,3,9)); g_assert (G_IN_SET ('x', 'a', 'x', 'c')); - g_assert (!G_IN_SET ('y', 'a', 'x', 'c')) + g_assert (!G_IN_SET ('y', 'a', 'x', 'c')); } int main (int argc, char **argv) From caa51ac24ffcdffcb610bc6ccc9da964d4be74ee Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 14 Jun 2017 17:00:25 -0400 Subject: [PATCH 124/280] glnx-macros.h: add GLNX_HASH_TABLE_FOREACH macros These macros make it much easier to iterate over a GHashTable. It takes care of initializing an iterator and casting keys and values to their proper types. See the example usage in the docstring for more info. --- glnx-macros.h | 58 +++++++++++++++++++++++++++++++++++++ tests/test-libglnx-macros.c | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/glnx-macros.h b/glnx-macros.h index 87c25cad..cee068d4 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -111,5 +111,63 @@ G_BEGIN_DECLS #endif /* ifndef G_IN_SET */ +#define _GLNX_CONCAT(a, b) a##b +#define _GLNX_CONCAT_INDIRECT(a, b) _GLNX_CONCAT(a, b) +#define _GLNX_MAKE_ANONYMOUS(a) _GLNX_CONCAT_INDIRECT(a, __COUNTER__) + +#define _GLNX_HASH_TABLE_FOREACH_IMPL_KV(guard, ht, it, kt, k, vt, v) \ + gboolean guard = TRUE; \ + for (GHashTableIter it; \ + guard && ({ g_hash_table_iter_init (&it, ht), TRUE; }); \ + guard = FALSE) \ + for (kt k; guard; guard = FALSE) \ + for (vt v; g_hash_table_iter_next (&it, (gpointer)&k, (gpointer)&v);) + + +/* Cleaner method to iterate over a GHashTable. I.e. rather than + * + * gpointer k, v; + * GHashTableIter it; + * g_hash_table_iter_init (&it, table); + * while (g_hash_table_iter_next (&it, &k, &v)) + * { + * const char *str = k; + * GPtrArray *arr = v; + * ... + * } + * + * you can simply do + * + * GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, str, GPtrArray*, arr) + * { + * ... + * } + * + * All variables are scoped within the loop. You may use the `it` variable as + * usual, e.g. to remove an element using g_hash_table_iter_remove(&it). There + * are shorter variants for the more common cases where you do not need access + * to the iterator or to values: + * + * GLNX_HASH_TABLE_FOREACH (table, const char*, str) { ... } + * GLNX_HASH_TABLE_FOREACH_KV (table, const char*, str, MyData*, data) { ... } + * + */ +#define GLNX_HASH_TABLE_FOREACH_IT(ht, it, kt, k, vt, v) \ + _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, it, kt, k, vt, v) + +/* Variant of GLNX_HASH_TABLE_FOREACH without having to specify an iterator. An + * anonymous iterator will be created. */ +#define GLNX_HASH_TABLE_FOREACH_KV(ht, kt, k, vt, v) \ + _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, vt, v) + +/* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking vals. */ +#define GLNX_HASH_TABLE_FOREACH(ht, kt, k) \ + _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, \ + gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_)) G_END_DECLS diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index d45c5cf8..2a115fca 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -40,9 +40,62 @@ test_inset (void) g_assert (!G_IN_SET ('y', 'a', 'x', 'c')); } +static void +test_hash_table_foreach (void) +{ + /* use var names all different from the macro metavars to ensure proper + * substitution */ + g_autoptr(GHashTable) table = g_hash_table_new (g_str_hash, g_str_equal); + const char *keys[] = {"key1", "key2"}; + const char *vals[] = {"val1", "val2"}; + g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]); + g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]); + + guint i = 0; + GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val) + { + g_assert_cmpstr (key, ==, keys[i]); + g_assert_cmpstr (val, ==, vals[i]); + i++; + } + g_assert_cmpuint (i, ==, 2); + + i = 0; + GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val) + { + g_hash_table_iter_remove (&it); + break; + } + g_assert_cmpuint (g_hash_table_size (table), ==, 1); + + g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]); + g_assert_cmpuint (g_hash_table_size (table), ==, 1); + + g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]); + g_assert_cmpuint (g_hash_table_size (table), ==, 2); + + i = 0; + GLNX_HASH_TABLE_FOREACH_KV (table, const char*, key, const char*, val) + { + g_assert_cmpstr (key, ==, keys[i]); + g_assert_cmpstr (val, ==, vals[i]); + i++; + } + g_assert_cmpuint (i, ==, 2); + + i = 0; + GLNX_HASH_TABLE_FOREACH (table, const char*, key) + { + g_assert_cmpstr (key, ==, keys[i]); + i++; + } + g_assert_cmpuint (i, ==, 2); +} + int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/inset", test_inset); + g_test_add_func ("/hash_table_foreach", test_hash_table_foreach); return g_test_run(); } From 4d34066a2ff5d806db35c3ac765f87ace460fa7e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 26 Jun 2017 13:04:35 -0400 Subject: [PATCH 125/280] fdio: Add wrappers for renameat(), unlinkat() Besides doing `TEMP_FAILURE_RETRY` and `GError` conversion, these also prefix the error with arguments. --- glnx-fdio.c | 6 ++---- glnx-fdio.h | 37 ++++++++++++++++++++++++++++++++++++- tests/test-libglnx-fdio.c | 7 ++----- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index e496828e..b5eaa9ee 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -319,15 +319,13 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, "Exhausted %u attempts to create temporary file", count); return FALSE; } - if (renameat (target_dfd, tmpname_buf, target_dfd, target) < 0) + if (!glnx_renameat (target_dfd, tmpname_buf, target_dfd, target, error)) { /* This is currently the only case where we need to have * a cleanup unlinkat() still with O_TMPFILE. */ - int errsv = errno; (void) unlinkat (target_dfd, tmpname_buf, 0); - errno = errsv; - return glnx_throw_errno_prefix (error, "renameat"); + return FALSE; } } else diff --git a/glnx-fdio.h b/glnx-fdio.h index 95574bd8..bdccbe55 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -27,6 +27,7 @@ #include #include #include +#include #include /* From systemd/src/shared/util.h */ /* When we include libgen.h because we need dirname() we immediately @@ -244,8 +245,42 @@ glnx_fstatat (int dfd, GError **error) { if (TEMP_FAILURE_RETRY (fstatat (dfd, path, buf, flags)) != 0) - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "fstatat(%s)", path); + return TRUE; +} + +/** + * glnx_renameat: + * + * Wrapper around renameat() which adds #GError support and ensures that it + * retries on %EINTR. + */ +static inline gboolean +glnx_renameat (int src_dfd, + const gchar *src_path, + int dest_dfd, + const gchar *dest_path, + GError **error) +{ + if (TEMP_FAILURE_RETRY (renameat (src_dfd, src_path, dest_dfd, dest_path)) != 0) + return glnx_throw_errno_prefix (error, "renameat(%s, %s)", src_path, dest_path); + return TRUE; +} +/** + * glnx_unlinkat: + * + * Wrapper around unlinkat() which adds #GError support and ensures that it + * retries on %EINTR. + */ +static inline gboolean +glnx_unlinkat (int dfd, + const gchar *path, + int flags, + GError **error) +{ + if (TEMP_FAILURE_RETRY (unlinkat (dfd, path, flags)) != 0) + return glnx_throw_errno_prefix (error, "unlinkat(%s)", path); return TRUE; } diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 9830c107..bc2d7ac1 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -80,11 +80,8 @@ test_renameat2_noreplace (void) glnx_set_error_from_errno (error); goto out; } - if (fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } + if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW)) + goto out; if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) == 0) g_assert_not_reached (); From 5ab15ac175ca45fce1b63a910f8ae2c488b92cfe Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 27 Jun 2017 20:07:16 -0700 Subject: [PATCH 126/280] macros: add GLNX_HASH_TABLE_FOREACH_V Looking at converting the ostree codebase, iterating over only the values of a hash table (while ignoring the key) is actually a more common pattern than I thought. So let's give it its own macro as well so users don't have to resort to the _KV variant. --- glnx-macros.h | 11 ++++++++++- tests/test-libglnx-macros.c | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/glnx-macros.h b/glnx-macros.h index cee068d4..b5127b74 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -146,9 +146,10 @@ G_BEGIN_DECLS * All variables are scoped within the loop. You may use the `it` variable as * usual, e.g. to remove an element using g_hash_table_iter_remove(&it). There * are shorter variants for the more common cases where you do not need access - * to the iterator or to values: + * to the iterator or to keys/values: * * GLNX_HASH_TABLE_FOREACH (table, const char*, str) { ... } + * GLNX_HASH_TABLE_FOREACH_V (table, MyData*, data) { ... } * GLNX_HASH_TABLE_FOREACH_KV (table, const char*, str, MyData*, data) { ... } * */ @@ -163,6 +164,14 @@ G_BEGIN_DECLS _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \ _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, vt, v) +/* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking keys. */ +#define GLNX_HASH_TABLE_FOREACH_V(ht, vt, v) \ + _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \ + _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), \ + gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_), \ + vt, v) + /* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking vals. */ #define GLNX_HASH_TABLE_FOREACH(ht, kt, k) \ _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \ diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index 2a115fca..ffde8fae 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -90,6 +90,14 @@ test_hash_table_foreach (void) i++; } g_assert_cmpuint (i, ==, 2); + + i = 0; + GLNX_HASH_TABLE_FOREACH_V (table, const char*, val) + { + g_assert_cmpstr (val, ==, vals[i]); + i++; + } + g_assert_cmpuint (i, ==, 2); } int main (int argc, char **argv) From d4c5c02327cc3ac29e40848c53fd1bce392e8ae5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 27 Jun 2017 21:01:46 -0400 Subject: [PATCH 127/280] fdio: Be sure to unset tmpfile's initialized state on cleanup I'm not aware of a problem in practice here, but we should do this on general principle. Writing this patch now because I hit a fd leak in the ostree static delta processing that was introduced in the tmpfile prep code, but fixed in the final port. --- glnx-fdio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index b5eaa9ee..ad43e61d 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -145,10 +145,16 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, return 0; } -/* Deallocate a tmpfile */ +/* Deallocate a tmpfile, closing the fd and deleting the path, if any. This is + * normally called by default by the autocleanup attribute, but you can also + * invoke this directly. + */ void glnx_tmpfile_clear (GLnxTmpfile *tmpf) { + /* Support being passed NULL so we work nicely in a GPtrArray */ + if (!tmpf) + return; if (!tmpf->initialized) return; if (tmpf->fd == -1) @@ -160,6 +166,7 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf) (void) unlinkat (tmpf->src_dfd, tmpf->path, 0); g_free (tmpf->path); } + tmpf->initialized = FALSE; } /* Allocate a temporary file, using Linux O_TMPFILE if available. From e55fd8ee318b858c8fadd223157d60952746200f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 27 Jun 2017 21:09:53 -0400 Subject: [PATCH 128/280] fdio: Introduce glnx_open_anonymous_tmpfile() There was a user of this in the libostree static delta code. --- glnx-fdio.c | 23 +++++++++++++++++++++++ glnx-fdio.h | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index ad43e61d..c8da35e8 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -244,6 +244,27 @@ glnx_open_tmpfile_linkable_at (int dfd, return FALSE; } +/* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. + * Useful for true temporary storage. The fd will be allocated in /var/tmp to + * ensure maximum storage space. + */ +gboolean +glnx_open_anonymous_tmpfile (int flags, + GLnxTmpfile *out_tmpf, + GError **error) +{ + if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, "/var/tmp", flags, out_tmpf, error)) + return FALSE; + if (out_tmpf->path) + { + (void) unlinkat (out_tmpf->src_dfd, out_tmpf->path, 0); + g_clear_pointer (&out_tmpf->path, g_free); + } + out_tmpf->anonymous = TRUE; + out_tmpf->src_dfd = -1; + return TRUE; +} + /* Use this after calling glnx_open_tmpfile_linkable_at() to give * the file its final name (link into place). */ @@ -257,7 +278,9 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, const gboolean replace = (mode == GLNX_LINK_TMPFILE_REPLACE); const gboolean ignore_eexist = (mode == GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST); + g_return_val_if_fail (!tmpf->anonymous, FALSE); g_return_val_if_fail (tmpf->fd >= 0, FALSE); + g_return_val_if_fail (tmpf->src_dfd == AT_FDCWD || tmpf->src_dfd >= 0, FALSE); /* Unlike the original systemd code, this function also supports * replacing existing files. diff --git a/glnx-fdio.h b/glnx-fdio.h index bdccbe55..14ae57e5 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -52,6 +52,7 @@ const char *glnx_basename (const char *path) typedef struct { gboolean initialized; + gboolean anonymous; int src_dfd; int fd; char *path; @@ -59,6 +60,11 @@ typedef struct { void glnx_tmpfile_clear (GLnxTmpfile *tmpf); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear); +gboolean +glnx_open_anonymous_tmpfile (int flags, + GLnxTmpfile *out_tmpf, + GError **error); + gboolean glnx_open_tmpfile_linkable_at (int dfd, const char *subpath, From 71d875543c91b2b6dd710ce6c78696c6c332a1ae Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 27 Jun 2017 21:14:34 -0400 Subject: [PATCH 129/280] macros: Avoid scanning macros `g-ir-scanner` is confused by some of the syntax extensions in `G_IN_SET()`; none of this is applicable to bindings, so just skip it. --- glnx-macros.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/glnx-macros.h b/glnx-macros.h index b5127b74..24705afd 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -28,6 +28,9 @@ G_BEGIN_DECLS +/* All of these are for C only. */ +#ifndef __GI_SCANNER__ + /* Taken from https://github.com/systemd/systemd/src/basic/string-util.h * at revision v228-666-gcf6c8c4 */ @@ -179,4 +182,6 @@ G_BEGIN_DECLS _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, \ gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_)) +#endif /* GI_SCANNER */ + G_END_DECLS From 6c2967c1adc56748c24a270cb49d89f07cfde210 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 27 Jun 2017 21:16:38 -0400 Subject: [PATCH 130/280] fdio: Remove extra ';' in header This was confusing `g-ir-scanner`. --- glnx-fdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 14ae57e5..f459e93a 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -58,7 +58,7 @@ typedef struct { char *path; } GLnxTmpfile; void glnx_tmpfile_clear (GLnxTmpfile *tmpf); -G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear); +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpfile, glnx_tmpfile_clear) gboolean glnx_open_anonymous_tmpfile (int flags, From 01e934c18efdbac071ebc19a8a95916d324970c9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 27 Jun 2017 21:17:58 -0400 Subject: [PATCH 131/280] tests: Fix compilation of fdio test Not sure how I missed this before. --- tests/test-libglnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index bc2d7ac1..311e7f0f 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -80,7 +80,7 @@ test_renameat2_noreplace (void) glnx_set_error_from_errno (error); goto out; } - if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW)) + if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW, error)) goto out; if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) == 0) From a37e672739197b8a7f3bdfe3f17099fe402f9a98 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 30 Jun 2017 12:11:14 -0400 Subject: [PATCH 132/280] macros: Add a size check for hashtable iters If the user provides a less than pointer-sized type, we'll clobber other things on the stack. See https://github.com/ostreedev/ostree/pull/990/ --- glnx-macros.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-macros.h b/glnx-macros.h index 24705afd..1d4e1753 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -120,6 +120,8 @@ G_BEGIN_DECLS #define _GLNX_HASH_TABLE_FOREACH_IMPL_KV(guard, ht, it, kt, k, vt, v) \ gboolean guard = TRUE; \ + G_STATIC_ASSERT (sizeof (kt) == sizeof (void*)); \ + G_STATIC_ASSERT (sizeof (vt) == sizeof (void*)); \ for (GHashTableIter it; \ guard && ({ g_hash_table_iter_init (&it, ht), TRUE; }); \ guard = FALSE) \ From 452c371ff30e9c54cf9a0bcee421d4415085e8d9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 9 Jul 2017 21:30:14 -0400 Subject: [PATCH 133/280] fdio: Ensure O_TMPFILE is mode 0600 Work around an older glibc bug. --- glnx-fdio.c | 17 +++++++++++------ tests/test-libglnx-fdio.c | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index c8da35e8..09aa87f6 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -169,13 +169,12 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf) tmpf->initialized = FALSE; } -/* Allocate a temporary file, using Linux O_TMPFILE if available. +/* Allocate a temporary file, using Linux O_TMPFILE if available. The file mode + * will be 0600. + * * The result will be stored in @out_tmpf, which is caller allocated * so you can store it on the stack in common scenarios. * - * Note that with O_TMPFILE, the file mode will be `000`; you likely - * want to chmod it before calling glnx_link_tmpfile_at(). - * * The directory fd @dfd must live at least as long as the output @out_tmpf. */ gboolean @@ -185,6 +184,7 @@ glnx_open_tmpfile_linkable_at (int dfd, GLnxTmpfile *out_tmpf, GError **error) { + const guint mode = 0600; glnx_fd_close int fd = -1; int count; @@ -200,11 +200,16 @@ glnx_open_tmpfile_linkable_at (int dfd, * link_tmpfile() below to rename the result after writing the file * in full. */ #if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT) - fd = openat (dfd, subpath, O_TMPFILE|flags, 0600); + fd = openat (dfd, subpath, O_TMPFILE|flags, mode); if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP))) return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); if (fd != -1) { + /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523 + * See also https://github.com/ostreedev/ostree/issues/991 + */ + if (fchmod (fd, mode) < 0) + return glnx_throw_errno_prefix (error, "fchmod"); out_tmpf->initialized = TRUE; out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ out_tmpf->fd = glnx_steal_fd (&fd); @@ -221,7 +226,7 @@ glnx_open_tmpfile_linkable_at (int dfd, { glnx_gen_temp_name (tmp); - fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0600); + fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, mode); if (fd < 0) { if (errno == EEXIST) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 311e7f0f..cee7cb68 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -137,12 +137,36 @@ test_renameat2_exchange (void) g_assert_no_error (local_error); } +static void +test_tmpfile (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + g_auto(GLnxTmpfile) tmpf = { 0, }; + + if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, ".", O_WRONLY|O_CLOEXEC, &tmpf, error)) + goto out; + + if (glnx_loop_write (tmpf.fd, "foo", strlen ("foo")) < 0) + { + (void)glnx_throw_errno_prefix (error, "write"); + goto out; + } + + if (glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE, AT_FDCWD, "foo", error)) + goto out; + + out: + g_assert_no_error (local_error); +} + int main (int argc, char **argv) { int ret; g_test_init (&argc, &argv, NULL); + g_test_add_func ("/tmpfile", test_tmpfile); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); From 210bcfcb65eb50ffef9cb541424dbd3b9b84997e Mon Sep 17 00:00:00 2001 From: Matthew Leeds Date: Thu, 13 Jul 2017 15:43:48 -0700 Subject: [PATCH 134/280] README.md: Change xdg-app to flatpak --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 241f5cef..8fb2faa4 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Why? ---- There are multiple projects which have a hard dependency on Linux and -GLib, such as NetworkManager, ostree, xdg-app, etc. It makes sense +GLib, such as NetworkManager, ostree, flatpak, etc. It makes sense for them to be able to share Linux-specific APIs. This module also contains some code taken from systemd, which has very From e30a773f2ca45ed7c0654a7c0b2a6449d3de9af0 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 10 Jul 2017 12:40:33 -0400 Subject: [PATCH 135/280] fdio: Add cleanup+flush API for FILE* Mostly in ostree/rpm-ostree, we work in either raw `int fd`, or `G{Input,Output}Stream`. One exception is the rpm-ostree `/etc/passwd` handling, which uses `FILE*` since that's what glibc exposes. And in general, there are use cases for `FILE*`; the raw `GUnixOutputStream` for example isn't buffered, and doing so via e.g. `GBufferedOutputStream` means allocating *two* GObjects and even worse going through multiple vfuncs for every write. `FILE*` is used heavily in systemd, and provides buffering. It is a bit cheaper than gobjects, but has its own trap; by default every operation locks a mutex. For more information on that, see `unlocked_stdio(3)`. However, callers can avoid that by using e.g. `fwrite_unlocked`, which I plan to do for most users of `FILE*` that aren't writing to one of the standard streams like `stdout` etc. --- glnx-fdio.c | 9 +++++++++ glnx-fdio.h | 17 +++++++++++++++++ tests/test-libglnx-fdio.c | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 09aa87f6..ffe5400d 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -53,6 +53,15 @@ sizeof(type) <= 4 ? 10 : \ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) +gboolean +glnx_stdio_file_flush (FILE *f, GError **error) +{ + if (fflush (f) != 0) + return glnx_throw_errno_prefix (error, "fflush"); + if (ferror (f) != 0) + return glnx_throw_errno_prefix (error, "ferror"); + return TRUE; +} /* An implementation of renameat2(..., RENAME_NOREPLACE) * with fallback to a non-atomic version. diff --git a/glnx-fdio.h b/glnx-fdio.h index f459e93a..150b22e3 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -50,6 +50,23 @@ const char *glnx_basename (const char *path) return (basename) (path); } +/* Utilities for standard FILE* */ +static inline void +glnx_stdio_file_cleanup (void *filep) +{ + FILE *f = filep; + if (f) + fclose (f); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, glnx_stdio_file_cleanup) + +/** + * glnx_stdio_file_flush: + * Call fflush() and check ferror(). + */ +gboolean +glnx_stdio_file_flush (FILE *f, GError **error); + typedef struct { gboolean initialized; gboolean anonymous; diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index cee7cb68..d4e62723 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -160,6 +160,28 @@ test_tmpfile (void) g_assert_no_error (local_error); } +static void +test_stdio_file (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + g_auto(GLnxTmpfile) tmpf = { 0, }; + if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error)) + goto out; + + g_autoptr(FILE) f = fdopen (tmpf.fd, "w"); + if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello")) + { + (void)glnx_throw_errno_prefix (error, "fwrite"); + goto out; + } + if (!glnx_stdio_file_flush (f, error)) + goto out; + + out: + g_assert_no_error (local_error); +} + int main (int argc, char **argv) { int ret; @@ -167,6 +189,7 @@ int main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_test_add_func ("/tmpfile", test_tmpfile); + g_test_add_func ("/stdio-file", test_stdio_file); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); From 8b75c8e341279032e34a1d496b2181362db3e6b8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 11 Jul 2017 10:30:05 -0400 Subject: [PATCH 136/280] Remove glnx_stream_fstat() There are only two users of this in ostree, and one of them is fairly bogus; we can just use `fstat()`. --- glnx-fdio.c | 25 ------------------------- glnx-fdio.h | 5 ----- 2 files changed, 30 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index ffe5400d..797d939c 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1022,28 +1022,3 @@ glnx_file_replace_contents_with_perms_at (int dfd, return TRUE; } - -/** - * glnx_stream_fstat: - * @stream: A stream containing a Unix file descriptor - * @stbuf: Memory location to write stat buffer - * @error: - * - * Some streams created via libgsystem are #GUnixInputStream; these do - * not support e.g. g_file_input_stream_query_info(). This function - * allows dropping to the raw unix fstat() call for these types of - * streams, while still conveniently wrapped with the normal GLib - * handling of @error. - */ -gboolean -glnx_stream_fstat (GFileDescriptorBased *stream, - struct stat *stbuf, - GError **error) -{ - int fd = g_file_descriptor_based_get_fd (stream); - - if (fstat (fd, stbuf) == -1) - return glnx_throw_errno_prefix (error, "fstat"); - - return TRUE; -} diff --git a/glnx-fdio.h b/glnx-fdio.h index 150b22e3..601e7936 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -181,11 +181,6 @@ glnx_file_copy_at (int src_dfd, GCancellable *cancellable, GError **error); -gboolean -glnx_stream_fstat (GFileDescriptorBased *stream, - struct stat *stbuf, - GError **error); - int glnx_renameat2_noreplace (int olddirfd, const char *oldpath, int newdirfd, const char *newpath); int glnx_renameat2_exchange (int olddirfd, const char *oldpath, From 547bcea280b389b244d52bf052f627e31bf210a9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 11 Jul 2017 10:32:11 -0400 Subject: [PATCH 137/280] fdio: Add a fchmod wrapper There are a number of versions of this in ostree at least, might as well wrap it. --- glnx-fdio.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index 601e7936..d6352b23 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -241,6 +241,28 @@ glnx_fstat (int fd, return TRUE; } +/** + * glnx_fchmod: + * @fd: FD + * @mode: Mode + * @error: Return location for a #GError, or %NULL + * + * Wrapper around fchmod() which adds #GError support and ensures that it + * retries on %EINTR. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: UNRELEASED + */ +static inline gboolean +glnx_fchmod (int fd, + mode_t mode, + GError **error) +{ + if (TEMP_FAILURE_RETRY (fchmod (fd, mode)) != 0) + return glnx_throw_errno_prefix (error, "fchmod"); + return TRUE; +} + /** * glnx_fstatat: * @dfd: Directory FD to stat beneath From 61ef326ad8381ea08ad3045fd0c65f974684afde Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 11 Jul 2017 10:33:01 -0400 Subject: [PATCH 138/280] fdio: Add string prefix for glnx_fstat() For consistency. --- glnx-fdio.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index d6352b23..902f8d2c 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -236,8 +236,7 @@ glnx_fstat (int fd, GError **error) { if (TEMP_FAILURE_RETRY (fstat (fd, buf)) != 0) - return glnx_throw_errno (error); - + return glnx_throw_errno_prefix (error, "fstat"); return TRUE; } From 607f1775bb1c626cae1875a957a34802daebe81c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 12 Jul 2017 21:11:02 -0400 Subject: [PATCH 139/280] errors: Add GLNX_AUTO_PREFIX_ERROR In a lot of places in ostree, we end up prefixing errors in the *caller*. Often we only have 1-2 callers, and doing the error prefixing isn't too duplicative. But there are definitely cases where it's cleaner to do the prefixing in the callee. We have functions that aren't ported to new style for this reason (they still do the prefixing in `out:`). Introduce a cleanup-oriented version of error prefixing so we can port those functions too. --- glnx-errors.h | 30 ++++++++++++++++++++++++++ tests/test-libglnx-errors.c | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/glnx-errors.h b/glnx-errors.h index e26a513e..ecf7e85f 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -84,6 +84,36 @@ glnx_prefix_error (GError **error, const char *fmt, ...) #define glnx_prefix_error_null(error, args...) \ ({glnx_prefix_error (error, args); NULL;}) +/** + * GLNX_AUTO_PREFIX_ERROR: + * + * An autocleanup-based macro to automatically call `g_prefix_error()` (also with a colon+space `: `) + * when it goes out of scope. This is useful when one wants error strings built up by the callee + * function, not all callers. + * + * ``` + * gboolean start_http_request (..., GError **error) + * { + * GLNX_AUTO_PREFIX_ERROR("HTTP request", error) + * + * if (!libhttp_request_start (..., error)) + * return FALSE; + * ... + * return TRUE; + * ``` + */ +typedef struct { + const char *prefix; + GError **error; +} GLnxAutoErrorPrefix; +static inline void +glnx_cleanup_auto_prefix_error (GLnxAutoErrorPrefix *prefix) +{ + g_prefix_error (prefix->error, "%s: ", prefix->prefix); +} +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxAutoErrorPrefix, glnx_cleanup_auto_prefix_error) +#define GLNX_AUTO_PREFIX_ERROR(text, error) g_auto(GLnxAutoErrorPrefix) _GLNX_MAKE_ANONYMOUS(_glnxautoprefixerror_) = { text, error } + /* Set @error using the value of `g_strerror (errno)`. * * This function returns %FALSE so it can be used conveniently in a single diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 721818ba..4e91e02c 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -125,6 +125,48 @@ test_error_errno (void) g_assert_cmpint (fd, ==, -1); } +static void +test_error_auto_nothrow (GError **error) +{ + GLNX_AUTO_PREFIX_ERROR("foo", error); + /* Side effect to avoid otherwise empty function */ + g_assert_no_error (*error); +} + +static void +test_error_auto_throw (GError **error) +{ + GLNX_AUTO_PREFIX_ERROR("foo", error); + (void) glnx_throw (error, "oops"); +} + +static void +test_error_auto_throw_recurse (GError **error) +{ + GLNX_AUTO_PREFIX_ERROR("foo", error); + + if (TRUE) + { + GLNX_AUTO_PREFIX_ERROR("bar", error); + (void) glnx_throw (error, "oops"); + } +} + +static void +test_error_auto (void) +{ + g_autoptr(GError) error = NULL; + test_error_auto_nothrow (&error); + g_assert_no_error (error); + test_error_auto_throw (&error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, "foo: oops"); + g_clear_error (&error); + test_error_auto_throw_recurse (&error); + g_assert_nonnull (error); + g_assert_cmpstr (error->message, ==, "foo: bar: oops"); +} + int main (int argc, char **argv) { int ret; @@ -133,6 +175,7 @@ int main (int argc, char **argv) g_test_add_func ("/error-throw", test_error_throw); g_test_add_func ("/error-errno", test_error_errno); + g_test_add_func ("/error-auto", test_error_auto); ret = g_test_run(); From 23f7df15006f14ddc3bc2ddee690f7f8604c3ebe Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Jul 2017 09:30:13 -0400 Subject: [PATCH 140/280] dirfd: Add filename to glnx_opendirat() This showed up in https://github.com/projectatomic/rpm-ostree/issues/883 We'll have to audit callers to be sure to avoid double-prefixing. --- glnx-dirfd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index bb73b015..667a18dd 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -67,9 +67,7 @@ glnx_opendirat (int dfd, { int ret = glnx_opendirat_with_errno (dfd, path, follow); if (ret == -1) - { - return glnx_throw_errno_prefix (error, "openat"); - } + return glnx_throw_errno_prefix (error, "opendir(%s)", path); *out_fd = ret; return TRUE; } From 268ae488167617b919b8bb219f555e1f9133e234 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Jul 2017 09:40:23 -0400 Subject: [PATCH 141/280] fdio: Introduce glnx_openat_read() This is kind of long overdue. Reasons are the same as the other wrappers. I debated adding `O_NOFOLLOW` support but the use cases for that are pretty obscure, callers who want that can just use the syscall directly for now. --- glnx-fdio.c | 43 +++++++++++++++++++++++++++++-------- glnx-fdio.h | 7 ++++++ tests/test-libglnx-xattrs.c | 26 +++++++--------------- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 797d939c..756248d6 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -387,6 +387,35 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, return TRUE; } +/** + * glnx_openat_rdonly: + * @dfd: File descriptor for origin directory + * @path: Pathname, relative to @dfd + * @follow: Whether or not to follow symbolic links in the final component + * @out_fd: (out): File descriptor + * @error: Error + * + * Use openat() to open a file, with flags `O_RDONLY | O_CLOEXEC | O_NOCTTY`. + * Like the other libglnx wrappers, will use `TEMP_FAILURE_RETRY` and + * also includes @path in @error in case of failure. + */ +gboolean +glnx_openat_rdonly (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error) +{ + int flags = O_RDONLY | O_CLOEXEC | O_NOCTTY; + if (!follow) + flags |= O_NOFOLLOW; + int fd = TEMP_FAILURE_RETRY (openat (dfd, path, flags)); + if (fd == -1) + return glnx_throw_errno_prefix (error, "openat(%s)", path); + *out_fd = fd; + return TRUE; +} + static guint8* glnx_fd_readall_malloc (int fd, gsize *out_len, @@ -524,9 +553,9 @@ glnx_file_get_contents_utf8_at (int dfd, { dfd = glnx_dirfd_canonicalize (dfd); - glnx_fd_close int fd = TEMP_FAILURE_RETRY (openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC)); - if (G_UNLIKELY (fd == -1)) - return glnx_null_throw_errno_prefix (error, "open(%s)", subpath); + glnx_fd_close int fd = -1; + if (!glnx_openat_rdonly (dfd, subpath, TRUE, &fd, error)) + return NULL; gsize len; g_autofree char *buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error); @@ -822,12 +851,8 @@ glnx_file_copy_at (int src_dfd, goto out; } - src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW)); - if (src_fd == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) + goto out; dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY; if (!(copyflags & GLNX_FILE_COPY_OVERWRITE)) diff --git a/glnx-fdio.h b/glnx-fdio.h index 902f8d2c..6b873e94 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -102,6 +102,13 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, const char *target, GError **error); +gboolean +glnx_openat_rdonly (int dfd, + const char *path, + gboolean follow, + int *out_fd, + GError **error); + GBytes * glnx_fd_readall_bytes (int fd, GCancellable *cancellable, diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index b6f0ac69..37c982d7 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -107,22 +107,17 @@ do_write_run (GLnxDirFdIterator *dfd_iter, GError **error) { while (TRUE) { - g_autoptr(GVariant) current_xattrs = NULL; - glnx_fd_close int fd = -1; - struct dirent *dent; if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error)) return FALSE; if (!dent) break; - fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC); - if (fd < 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } + glnx_fd_close int fd = -1; + if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error)) + return FALSE; + g_autoptr(GVariant) current_xattrs = NULL; if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error)) return FALSE; @@ -156,22 +151,17 @@ do_read_run (GLnxDirFdIterator *dfd_iter, guint nattrs = 0; while (TRUE) { - g_autoptr(GVariant) current_xattrs = NULL; - glnx_fd_close int fd = -1; - struct dirent *dent; if (!glnx_dirfd_iterator_next_dent (dfd_iter, &dent, NULL, error)) return FALSE; if (!dent) break; - fd = openat (dfd_iter->fd, dent->d_name, O_RDONLY | O_CLOEXEC); - if (fd < 0) - { - glnx_set_error_from_errno (error); - return FALSE; - } + glnx_fd_close int fd = -1; + if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error)) + return FALSE; + g_autoptr(GVariant) current_xattrs = NULL; if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error)) return FALSE; From 1c0bfd24b18d67a6fa4bc42185cdc8d5af3cb804 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Jul 2017 11:57:35 -0400 Subject: [PATCH 142/280] dirfd: Add glnx_ensure_dir() Another one where we have a lot of inlines in ostree at least. Not the same as `glnx_shutil_mkdir_p_at()` since in these cases we don't want automatic intermediate dirs, and it's cheaper to just call `mkdirat()` and handle `EEXIST` rather than do a `stat()` first. --- glnx-dirfd.h | 28 ++++++++++++++++++++++++++++ glnx-shutil.c | 2 ++ 2 files changed, 30 insertions(+) diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 581b3032..4824bc4b 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -83,6 +83,34 @@ char *glnx_fdrel_abspath (int dfd, void glnx_gen_temp_name (gchar *tmpl); +/** + * glnx_ensure_dir: + * @dfd: directory fd + * @path: Directory path + * @mode: Mode + * @error: Return location for a #GError, or %NULL + * + * Wrapper around mkdirat() which ignores adds #GError support, ensures that + * it retries on %EINTR, and also ignores `EEXIST`. + * + * See also `glnx_shutil_mkdir_p_at()` for recursive handling. + * + * Returns: %TRUE on success, %FALSE otherwise + */ +static inline gboolean +glnx_ensure_dir (int dfd, + const char *path, + mode_t mode, + GError **error) +{ + if (TEMP_FAILURE_RETRY (mkdirat (dfd, path, mode)) != 0) + { + if (G_UNLIKELY (errno != EEXIST)) + return glnx_throw_errno_prefix (error, "mkdirat(%s)", path); + } + return TRUE; +} + gboolean glnx_mkdtempat (int dfd, gchar *tmpl, int mode, diff --git a/glnx-shutil.c b/glnx-shutil.c index 2e02eea4..9124d7a4 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -185,6 +185,8 @@ mkdir_p_at_internal (int dfd, * * Similar to g_mkdir_with_parents(), except operates relative to the * directory fd @dfd. + * + * See also glnx_ensure_dir() for a non-recursive version. */ gboolean glnx_shutil_mkdir_p_at (int dfd, From 1468b70dbf19fc48cfbf0b19b7c7ffa9157cfb84 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 20 Jul 2017 15:11:32 -0400 Subject: [PATCH 143/280] dirfd: Add missing includes for errno Thought the previous patch would have been obvious enough not to compile test but... --- glnx-dirfd.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 4824bc4b..8e582fc1 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -21,6 +21,8 @@ #pragma once #include +#include +#include #include #include #include From 7d6a31fb9316ec85d462c26b84fb8fe9ca04ef2b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 20 Jul 2017 15:09:16 -0400 Subject: [PATCH 144/280] errors: Mark GLNX_AUTO_PREFIX_ERROR() as used Since it's intentional we never use it, and `clang` barfs on this (rightly). --- glnx-errors.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-errors.h b/glnx-errors.h index ecf7e85f..07c30fed 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -112,7 +112,8 @@ glnx_cleanup_auto_prefix_error (GLnxAutoErrorPrefix *prefix) g_prefix_error (prefix->error, "%s: ", prefix->prefix); } G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxAutoErrorPrefix, glnx_cleanup_auto_prefix_error) -#define GLNX_AUTO_PREFIX_ERROR(text, error) g_auto(GLnxAutoErrorPrefix) _GLNX_MAKE_ANONYMOUS(_glnxautoprefixerror_) = { text, error } +#define GLNX_AUTO_PREFIX_ERROR(text, error) \ + G_GNUC_UNUSED g_auto(GLnxAutoErrorPrefix) _GLNX_MAKE_ANONYMOUS(_glnxautoprefixerror_) = { text, error } /* Set @error using the value of `g_strerror (errno)`. * From c820571bc4389515902e268bea5489dc114c3973 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 21 Jul 2017 14:07:12 -0700 Subject: [PATCH 145/280] errors: check for an error before prefixing Minor tweak to the new `GLNX_AUTO_PREFIX_ERROR`. Since the common case is that there's no errors, let's bring down the same check that `g_prefix_error` does to avoid a function call most of the time. --- glnx-errors.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/glnx-errors.h b/glnx-errors.h index 07c30fed..5a6fe196 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -94,7 +94,7 @@ glnx_prefix_error (GError **error, const char *fmt, ...) * ``` * gboolean start_http_request (..., GError **error) * { - * GLNX_AUTO_PREFIX_ERROR("HTTP request", error) + * GLNX_AUTO_PREFIX_ERROR ("HTTP request", error) * * if (!libhttp_request_start (..., error)) * return FALSE; @@ -109,7 +109,8 @@ typedef struct { static inline void glnx_cleanup_auto_prefix_error (GLnxAutoErrorPrefix *prefix) { - g_prefix_error (prefix->error, "%s: ", prefix->prefix); + if (prefix->error && *(prefix->error)) + g_prefix_error (prefix->error, "%s: ", prefix->prefix); } G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxAutoErrorPrefix, glnx_cleanup_auto_prefix_error) #define GLNX_AUTO_PREFIX_ERROR(text, error) \ From ea6df95f22c8f2973714bdbb8b1accc4e37d4d56 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 21 Jul 2017 18:08:23 -0400 Subject: [PATCH 146/280] tests: Fix a -Wmaybe-uninitialized warning It'd be really nice if gtest had a variant which had the funcs take `GError`. May work on that. --- tests/test-libglnx-fdio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index d4e62723..16b66926 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -166,10 +166,17 @@ test_stdio_file (void) g_autoptr(GError) local_error = NULL; GError **error = &local_error; g_auto(GLnxTmpfile) tmpf = { 0, }; + g_autoptr(FILE) f = NULL; + if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error)) goto out; + f = fdopen (tmpf.fd, "w"); + if (!f) + { + (void)glnx_throw_errno_prefix (error, "fdopen"); + goto out; + } - g_autoptr(FILE) f = fdopen (tmpf.fd, "w"); if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello")) { (void)glnx_throw_errno_prefix (error, "fwrite"); From 50a0feaba03ffa5a1980d3a14487276b8f49f8a6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 26 Jul 2017 16:03:27 -0400 Subject: [PATCH 147/280] localalloc: Abort on EBADF from close() by default systemd does this by default. I think we should treat this as a fatal error since it can cause really painful-to-debug problems if we don't just get EBADF but actually close something else's fd due to a race. --- glnx-local-alloc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index 8c1914cf..af8d72f0 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -204,7 +204,8 @@ glnx_cleanup_close_fdp (int *fdp) if (fd >= 0) { errsv = errno; - (void) close (fd); + if (close (fd) < 0) + g_assert (errno != EBADF); errno = errsv; } } From a46752edcdb176f94d4681de2df91d0a2ba97539 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 31 Jul 2017 13:08:12 -0400 Subject: [PATCH 148/280] local-alloc: Remove almost all macros like glnx_free, glnx_unref_variant We should be able to rely upstream on everything *except* `glnx_unref_object` which requires the library itself to depend on a newer glib, which isn't true for e.g. RHEL7 libsoup. libostree was almost ready for this; just a few patches to push it to completion in https://github.com/ostreedev/ostree/pull/1042 --- glnx-local-alloc.h | 167 +++------------------------------------------ glnx-xattrs.c | 4 +- 2 files changed, 10 insertions(+), 161 deletions(-) diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index af8d72f0..46dd9d25 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -25,30 +25,6 @@ G_BEGIN_DECLS -#define GLNX_DEFINE_CLEANUP_FUNCTION(Type, name, func) \ - static inline void name (void *v) \ - { \ - func (*(Type*)v); \ - } - -#define GLNX_DEFINE_CLEANUP_FUNCTION0(Type, name, func) \ - static inline void name (void *v) \ - { \ - if (*(Type*)v) \ - func (*(Type*)v); \ - } - -/** - * glnx_free: - * - * Call g_free() on a variable location when it goes out of scope. - */ -#define glnx_free __attribute__ ((cleanup(glnx_local_free))) -#ifdef GLNX_GSYSTEM_COMPAT -#define gs_free __attribute__ ((cleanup(glnx_local_free))) -#endif -GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free) - /** * glnx_unref_object: * @@ -57,141 +33,14 @@ GLNX_DEFINE_CLEANUP_FUNCTION(void*, glnx_local_free, g_free) * %NULL. */ #define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) -#ifdef GLNX_GSYSTEM_COMPAT -#define gs_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) -#endif -GLNX_DEFINE_CLEANUP_FUNCTION0(GObject*, glnx_local_obj_unref, g_object_unref) - -/** - * glnx_unref_variant: - * - * Call g_variant_unref() on a variable location when it goes out of - * scope. Note that unlike g_variant_unref(), the variable may be - * %NULL. - */ -#define glnx_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) -#ifdef GLNX_GSYSTEM_COMPAT -#define gs_unref_variant __attribute__ ((cleanup(glnx_local_variant_unref))) -#endif -GLNX_DEFINE_CLEANUP_FUNCTION0(GVariant*, glnx_local_variant_unref, g_variant_unref) - -/** - * glnx_free_variant_iter: - * - * Call g_variant_iter_free() on a variable location when it goes out of - * scope. - */ -#define glnx_free_variant_iter __attribute__ ((cleanup(glnx_local_variant_iter_free))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, glnx_local_variant_iter_free, g_variant_iter_free) - -/** - * glnx_free_variant_builder: - * - * Call g_variant_builder_unref() on a variable location when it goes out of - * scope. - */ -#define glnx_unref_variant_builder __attribute__ ((cleanup(glnx_local_variant_builder_unref))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, glnx_local_variant_builder_unref, g_variant_builder_unref) - -/** - * glnx_unref_array: - * - * Call g_array_unref() on a variable location when it goes out of - * scope. Note that unlike g_array_unref(), the variable may be - * %NULL. - - */ -#define glnx_unref_array __attribute__ ((cleanup(glnx_local_array_unref))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GArray*, glnx_local_array_unref, g_array_unref) - -/** - * glnx_unref_ptrarray: - * - * Call g_ptr_array_unref() on a variable location when it goes out of - * scope. Note that unlike g_ptr_array_unref(), the variable may be - * %NULL. - - */ -#define glnx_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) -#ifdef GLNX_GSYSTEM_COMPAT -#define gs_unref_ptrarray __attribute__ ((cleanup(glnx_local_ptrarray_unref))) -#endif -GLNX_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, glnx_local_ptrarray_unref, g_ptr_array_unref) - -/** - * glnx_unref_hashtable: - * - * Call g_hash_table_unref() on a variable location when it goes out - * of scope. Note that unlike g_hash_table_unref(), the variable may - * be %NULL. - */ -#define glnx_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) -#ifdef GLNX_GSYSTEM_COMPAT -#define gs_unref_hashtable __attribute__ ((cleanup(glnx_local_hashtable_unref))) -#endif -GLNX_DEFINE_CLEANUP_FUNCTION0(GHashTable*, glnx_local_hashtable_unref, g_hash_table_unref) - -/** - * glnx_free_list: - * - * Call g_list_free() on a variable location when it goes out - * of scope. - */ -#define glnx_free_list __attribute__ ((cleanup(glnx_local_free_list))) -GLNX_DEFINE_CLEANUP_FUNCTION(GList*, glnx_local_free_list, g_list_free) - -/** - * glnx_free_slist: - * - * Call g_slist_free() on a variable location when it goes out - * of scope. - */ -#define glnx_free_slist __attribute__ ((cleanup(glnx_local_free_slist))) -GLNX_DEFINE_CLEANUP_FUNCTION(GSList*, glnx_local_free_slist, g_slist_free) - -/** - * glnx_free_checksum: - * - * Call g_checksum_free() on a variable location when it goes out - * of scope. Note that unlike g_checksum_free(), the variable may - * be %NULL. - */ -#define glnx_free_checksum __attribute__ ((cleanup(glnx_local_checksum_free))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GChecksum*, glnx_local_checksum_free, g_checksum_free) - -/** - * glnx_unref_bytes: - * - * Call g_bytes_unref() on a variable location when it goes out - * of scope. Note that unlike g_bytes_unref(), the variable may - * be %NULL. - */ -#define glnx_unref_bytes __attribute__ ((cleanup(glnx_local_bytes_unref))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GBytes*, glnx_local_bytes_unref, g_bytes_unref) - -/** - * glnx_strfreev: - * - * Call g_strfreev() on a variable location when it goes out of scope. - */ -#define glnx_strfreev __attribute__ ((cleanup(glnx_local_strfreev))) -GLNX_DEFINE_CLEANUP_FUNCTION(char**, glnx_local_strfreev, g_strfreev) - -/** - * glnx_free_error: - * - * Call g_error_free() on a variable location when it goes out of scope. - */ -#define glnx_free_error __attribute__ ((cleanup(glnx_local_free_error))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GError*, glnx_local_free_error, g_error_free) - -/** - * glnx_unref_keyfile: - * - * Call g_key_file_unref() on a variable location when it goes out of scope. - */ -#define glnx_unref_keyfile __attribute__ ((cleanup(glnx_local_keyfile_unref))) -GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_unref) +static inline void +glnx_local_obj_unref (void *v) +{ + GObject *o = *(GObject **)v; + if (o) + g_object_unref (o); +} +#define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) static inline void glnx_cleanup_close_fdp (int *fdp) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 3ade89c1..79a14cd3 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -145,8 +145,8 @@ get_xattrs_impl (const char *path, { gboolean ret = FALSE; ssize_t bytes_read, real_size; - glnx_free char *xattr_names = NULL; - glnx_free char *xattr_names_canonical = NULL; + g_autofree char *xattr_names = NULL; + g_autofree char *xattr_names_canonical = NULL; GVariantBuilder builder; gboolean builder_initialized = FALSE; g_autoptr(GVariant) ret_xattrs = NULL; From 29ad99c9b6103853e6d6bf42fa7bff335eb114a8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 1 Aug 2017 14:25:40 -0400 Subject: [PATCH 149/280] console: Fix Coverity NULL deref warning We need to handle our "empty to NULL canonicalization" before doing the length. Coverity CID: 1376570 --- glnx-console.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 416d2768..cf5409c9 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -187,11 +187,12 @@ text_percent_internal (const char *text, const guint n_spaces = sizeof (spaces) - 1; const guint ncolumns = glnx_console_columns (); const guint bar_min = 10; - const guint input_textlen = text ? strlen (text) : 0; if (text && !*text) text = NULL; + const guint input_textlen = text ? strlen (text) : 0; + if (percentage == current_percent && g_strcmp0 (text, current_text) == 0) return; From d18f026ee7316bc2dde291c55db49b69dcb76df3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 2 Aug 2017 14:32:06 -0400 Subject: [PATCH 150/280] fdio: Merge systemd code to use copy_file_range(), use FICLONE FICLONE is the new alias for the formerly btrfs-specific ioctl; XFS has experimental patches to support it. Further, we should use copy_file_range() for the case where we're only doing a limited copy. Both NFS and XFS (with reflink enabled) understand it. Part of the reason I'm doing this is so that ostree's `/etc` merge will start using XFS reflinks. But another major reason is to take the next step after and copy this code into GLib as well, so that all of the general GLib users will benefit; e.g. Nautilus will transparently do server copy offloads with NFS home directories. See also this coreutils thread about `copy_file_range()`: . I don't care about file holes for our use cases, so it's fine. Other changes while I'm here: - Tweak the sendfile() case to match the newly inlined logic for cfr - Add a TEMP_FAILURE_RETRY() around the read() --- glnx-fdio.c | 136 ++++++++++++++++++++++++++++++----------- glnx-fdio.h | 2 +- glnx-missing-syscall.h | 41 +++++++++++++ 3 files changed, 141 insertions(+), 38 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 756248d6..dc8459d9 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -31,9 +31,6 @@ #include #include #include -/* See linux.git/fs/btrfs/ioctl.h */ -#define BTRFS_IOCTL_MAGIC 0x94 -#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) #include #include @@ -43,6 +40,11 @@ #include #include +/* The standardized version of BTRFS_IOC_CLONE */ +#ifndef FICLONE +#define FICLONE _IOW(0x94, 9, int) +#endif + /* Returns the number of chars needed to format variables of the * specified type as a decimal string. Adds in extra space for a * negative '-' prefix (hence works correctly on signed @@ -654,13 +656,6 @@ copy_symlink_at (int src_dfd, * conveniently fit in with the rest of libglnx. */ -static int btrfs_reflink(int infd, int outfd) { - g_return_val_if_fail(infd >= 0, -1); - g_return_val_if_fail(outfd >= 0, -1); - - return ioctl (outfd, BTRFS_IOC_CLONE, infd); -} - /* Like write(), but loop until @nbytes are written, or an error * occurs. * @@ -703,8 +698,10 @@ glnx_loop_write(int fd, const void *buf, size_t nbytes) return 0; } -/* Read from @fdf until EOF, writing to @fdt. If @try_reflink is %TRUE, - * attempt to use any "reflink" functionality; see e.g. https://lwn.net/Articles/331808/ +/* Read from @fdf until EOF, writing to @fdt. If max_bytes is -1, a full-file + * clone will be attempted. Otherwise Linux copy_file_range(), sendfile() + * syscall will be attempted. If none of those work, this function will do a + * plain read()/write() loop. * * The file descriptor @fdf must refer to a regular file. * @@ -712,58 +709,123 @@ glnx_loop_write(int fd, const void *buf, size_t nbytes) * On error, this function returns `-1` and @errno will be set. */ int -glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink) +glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) { - bool try_sendfile = true; - int r; + /* Last updates from systemd as of commit 6bda23dd6aaba50cf8e3e6024248cf736cc443ca */ + static int have_cfr = -1; /* -1 means unknown */ + bool try_cfr = have_cfr != 0; + static int have_sendfile = -1; /* -1 means unknown */ + bool try_sendfile = have_sendfile != 0; g_return_val_if_fail (fdf >= 0, -1); g_return_val_if_fail (fdt >= 0, -1); g_return_val_if_fail (max_bytes >= -1, -1); - /* Try btrfs reflinks first. */ - if (try_reflink && max_bytes == (off_t) -1) + /* If we've requested to copy the whole range, try a full-file clone first. + */ + if (max_bytes == (off_t) -1) { - r = btrfs_reflink(fdf, fdt); - if (r >= 0) + if (ioctl (fdt, FICLONE, fdf) == 0) return 0; /* Fall through */ + struct stat stbuf; + + /* Gather the size so we can provide the whole thing at once to + * copy_file_range() or sendfile(). + */ + if (fstat (fdf, &stbuf) < 0) + return -1; + max_bytes = stbuf.st_size; } while (TRUE) { - size_t m = COPY_BUFFER_SIZE; ssize_t n; - if (max_bytes != (off_t) -1) + /* First, try copy_file_range(). Note this is an inlined version of + * try_copy_file_range() from systemd upstream, which works better since + * we use POSIX errno style. + */ + if (try_cfr) { - if ((off_t) m > max_bytes) - m = (size_t) max_bytes; + n = copy_file_range (fdf, NULL, fdt, NULL, max_bytes, 0u); + if (n < 0) + { + if (errno == ENOSYS) + { + /* No cfr in kernel, mark as permanently unavailable + * and fall through to sendfile(). + */ + have_cfr = 0; + try_cfr = false; + } + else if (errno == EXDEV) + /* We won't try cfr again for this run, but let's be + * conservative and not mark it as available/unavailable until + * we know for sure. + */ + try_cfr = false; + else + return -1; + } + else + { + /* cfr worked, mark it as available */ + if (have_cfr == -1) + have_cfr = 1; + + if (n == 0) /* EOF */ + break; + else + /* Success! */ + goto next; + } } - /* First try sendfile(), unless we already tried */ + /* Next try sendfile(); this version is also changed from systemd upstream + * to match the same logic we have for copy_file_range(). + */ if (try_sendfile) { - n = sendfile (fdt, fdf, NULL, m); + n = sendfile (fdt, fdf, NULL, max_bytes); if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) + if (G_IN_SET (errno, EINVAL, ENOSYS)) + { + /* No sendfile(), or it doesn't work on regular files. + * Mark it as permanently unavailable, and fall through + * to plain read()/write(). + */ + have_sendfile = 0; + try_sendfile = false; + } + else return -1; + } + else + { + /* sendfile() worked, mark it as available */ + if (have_sendfile == -1) + have_sendfile = 1; - try_sendfile = false; - /* use fallback below */ + if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; } - else if (n == 0) /* EOF */ - break; - else if (n > 0) - /* Succcess! */ - goto next; } /* As a fallback just copy bits by hand */ - { char buf[m]; + { size_t m = COPY_BUFFER_SIZE; + if (max_bytes != (off_t) -1) + { + if ((off_t) m > max_bytes) + m = (size_t) max_bytes; + } + char buf[m]; - n = read (fdf, buf, m); + n = TEMP_FAILURE_RETRY (read (fdf, buf, m)); if (n < 0) return -1; if (n == 0) /* EOF */ @@ -776,7 +838,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink next: if (max_bytes != (off_t) -1) { - g_assert(max_bytes >= n); + g_assert_cmpint (max_bytes, >=, n); max_bytes -= n; if (max_bytes == 0) break; @@ -867,7 +929,7 @@ glnx_file_copy_at (int src_dfd, goto out; } - r = glnx_regfile_copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE); + r = glnx_regfile_copy_bytes (src_fd, dest_fd, (off_t) -1); if (r < 0) { glnx_set_error_from_errno (error); diff --git a/glnx-fdio.h b/glnx-fdio.h index 6b873e94..4ee24afa 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -170,7 +170,7 @@ int glnx_loop_write (int fd, const void *buf, size_t nbytes); int -glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes, gboolean try_reflink); +glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes); typedef enum { GLNX_FILE_COPY_OVERWRITE = (1 << 0), diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index 2c583c6d..c4957e0c 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -52,3 +52,44 @@ static inline int renameat2(int oldfd, const char *oldname, int newfd, const cha # endif } #endif + +/* Copied from systemd git: + commit 6bda23dd6aaba50cf8e3e6024248cf736cc443ca + Author: Yu Watanabe + AuthorDate: Thu Jul 27 20:22:54 2017 +0900 + Commit: Zbigniew Jędrzejewski-Szmek + CommitDate: Thu Jul 27 07:22:54 2017 -0400 +*/ +#if !HAVE_DECL_COPY_FILE_RANGE +# ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined __s390__ +# define __NR_copy_file_range 375 +# elif defined __arm__ +# define __NR_copy_file_range 391 +# elif defined __aarch64__ +# define __NR_copy_file_range 285 +# elif defined __powerpc__ +# define __NR_copy_file_range 379 +# elif defined __arc__ +# define __NR_copy_file_range 285 +# else +# warning "__NR_copy_file_range not defined for your architecture" +# endif +# endif + +static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { +# ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif From 1893c1ff748ee39f33e7eef1c833ce47897ff95d Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 15 Aug 2017 12:04:45 +0100 Subject: [PATCH 151/280] glnx-console: Add missing NULL check before writing out text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s possible that text is NULL on this path. Coverity CID: 1376570 Signed-off-by: Philip Withnall --- glnx-console.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index cf5409c9..785f3480 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -222,7 +222,8 @@ text_percent_internal (const char *text, if (percentage == -1) { - fwrite (text, 1, input_textlen, stdout); + if (text != NULL) + fwrite (text, 1, input_textlen, stdout); /* Overwrite remaining space, if any */ if (ncolumns > input_textlen) From 6bd24baed27b0fb60ba923440d1a0c69fed8cf23 Mon Sep 17 00:00:00 2001 From: Matthew Leeds Date: Tue, 15 Aug 2017 13:17:03 -0700 Subject: [PATCH 152/280] dirfd: Fix typo in comment --- glnx-dirfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 667a18dd..29a3ff7c 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -83,7 +83,7 @@ typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator; /** * glnx_dirfd_iterator_init_at: * @dfd: File descriptor, may be AT_FDCWD or -1 - * @path: Path, may be relative to @df + * @path: Path, may be relative to @dfd * @follow: If %TRUE and the last component of @path is a symlink, follow it * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized * @error: Error From e226ccf6913d1d852fde1e150a99fab508f85c34 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 15 Aug 2017 15:23:58 -0400 Subject: [PATCH 153/280] console: trim useless check The `percentage` var is a guint and so is always >= 0. Coverity CID: 163703 --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 785f3480..c6d93311 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -275,7 +275,7 @@ void glnx_console_progress_text_percent (const char *text, guint percentage) { - g_return_if_fail (percentage >= 0 && percentage <= 100); + g_return_if_fail (percentage <= 100); text_percent_internal (text, percentage); } From 7100ebbc6800c7e5e2e09cefe067fbb88b32f10b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 18 Aug 2017 14:45:07 -0400 Subject: [PATCH 154/280] dirfd: New tmpdir API Basically all of the ostree/rpm-ostree callers want to both create and open, so let's merge `glnx_mkdtempat()` and `glnx_mkdtempat_open()`. Second, all of them want to do `glnx_shutil_rm_rf_at()` on cleanup, so we do the same thing we did with `GLnxTmpfile` and create `GLnxTmpDir` that has a cleanup attribute. The cleanup this results in for rpm-ostree is pretty substantial. --- glnx-dirfd.c | 131 +++++++++++++++++++----------------- glnx-dirfd.h | 25 ++++--- tests/test-libglnx-xattrs.c | 17 ++--- 3 files changed, 87 insertions(+), 86 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 29a3ff7c..b745e280 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -25,6 +25,7 @@ #include #include #include +#include /** * glnx_opendirat_with_errno: @@ -283,27 +284,36 @@ glnx_gen_temp_name (gchar *tmpl) /** * glnx_mkdtempat: * @dfd: Directory fd - * @tmpl: (type filename): template directory name, last 6 characters will be replaced - * @mode: permissions to create the temporary directory with + * @tmpl: (type filename): Initial template directory name, last 6 characters will be replaced + * @mode: permissions with which to create the temporary directory + * @out_tmpdir: (out caller-allocates): Initialized tempdir structure * @error: Error * - * Similar to g_mkdtemp_full, but using openat. + * Somewhat similar to g_mkdtemp_full(), but fd-relative, and returns a + * structure that uses autocleanups. Note that the supplied @dfd lifetime + * must match or exceed that of @out_tmpdir in order to remove the directory. */ gboolean -glnx_mkdtempat (int dfd, - gchar *tmpl, - int mode, - GError **error) +glnx_mkdtempat (int dfd, const char *tmpl, int mode, + GLnxTmpDir *out_tmpdir, GError **error) { - int count; + g_return_val_if_fail (tmpl != NULL, FALSE); + g_return_val_if_fail (out_tmpdir != NULL, FALSE); + g_return_val_if_fail (!out_tmpdir->initialized, FALSE); - g_return_val_if_fail (tmpl != NULL, -1); + dfd = glnx_dirfd_canonicalize (dfd); - for (count = 0; count < 100; count++) + g_autofree char *path = g_strdup (tmpl); + for (int count = 0; count < 100; count++) { - glnx_gen_temp_name (tmpl); - - if (mkdirat (dfd, tmpl, mode) == -1) + glnx_gen_temp_name (path); + + /* Ideally we could use openat(O_DIRECTORY | O_CREAT | O_EXCL) here + * to create and open the directory atomically, but that’s not supported by + * current kernel versions: http://www.openwall.com/lists/oss-security/2014/11/26/14 + * (Tested on kernel 4.10.10-200.fc25.x86_64). For the moment, accept a + * TOCTTOU race here. */ + if (mkdirat (dfd, path, mode) == -1) { if (errno == EEXIST) continue; @@ -314,77 +324,72 @@ glnx_mkdtempat (int dfd, return glnx_throw_errno_prefix (error, "mkdirat"); } + /* And open it */ + glnx_fd_close int ret_dfd = -1; + if (!glnx_opendirat (dfd, path, FALSE, &ret_dfd, error)) + { + /* If we fail to open, let's try to clean up */ + (void)unlinkat (dfd, path, AT_REMOVEDIR); + return FALSE; + } + + /* Return the initialized directory struct */ + out_tmpdir->initialized = TRUE; + out_tmpdir->src_dfd = dfd; /* referenced; see above docs */ + out_tmpdir->fd = glnx_steal_fd (&ret_dfd); + out_tmpdir->path = g_steal_pointer (&path); return TRUE; } + /* Failure */ g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, - "mkstempat ran out of combinations to try."); + "glnx_mkdtempat ran out of combinations to try"); return FALSE; } /** - * glnx_mkdtempat_open: - * @dfd: Directory FD - * @tmpl: (type filename): template directory name, last 6 characters will be replaced + * glnx_mkdtemp: + * @tmpl: (type filename): Source template directory name, last 6 characters will be replaced * @mode: permissions to create the temporary directory with - * @out_dfd: (out caller-allocates): Return location for an FD for the new - * temporary directory, or `-1` on error + * @out_tmpdir: (out caller-allocates): Return location for tmpdir data * @error: Return location for a #GError, or %NULL * - * Similar to glnx_mkdtempat(), except it will open the resulting temporary - * directory and return a directory FD to it. + * Similar to glnx_mkdtempat(), but will use g_get_tmp_dir() as the parent + * directory to @tmpl. * * Returns: %TRUE on success, %FALSE otherwise * Since: UNRELEASED */ gboolean -glnx_mkdtempat_open (int dfd, - gchar *tmpl, - int mode, - int *out_dfd, - GError **error) +glnx_mkdtemp (const gchar *tmpl, + int mode, + GLnxTmpDir *out_tmpdir, + GError **error) { - /* FIXME: Ideally we could use openat(O_DIRECTORY | O_CREAT | O_EXCL) here - * to create and open the directory atomically, but that’s not supported by - * current kernel versions: http://www.openwall.com/lists/oss-security/2014/11/26/14 - * (Tested on kernel 4.10.10-200.fc25.x86_64). For the moment, accept a - * TOCTTOU race here. */ - *out_dfd = -1; - - if (!glnx_mkdtempat (dfd, tmpl, mode, error)) - return FALSE; - - return glnx_opendirat (dfd, tmpl, FALSE, out_dfd, error); + g_autofree char *path = g_build_filename (g_get_tmp_dir (), tmpl, NULL); + return glnx_mkdtempat (AT_FDCWD, path, mode, + out_tmpdir, error); } -/** - * glnx_mkdtempat_open_in_system: - * @tmpl: (type filename): template directory name, last 6 characters will be replaced - * @mode: permissions to create the temporary directory with - * @out_dfd: (out caller-allocates): Return location for an FD for the new - * temporary directory, or `-1` on error - * @error: Return location for a #GError, or %NULL - * - * Similar to glnx_mkdtempat_open(), except it will use the system temporary - * directory (from g_get_tmp_dir()) as the parent directory to @tmpl. - * - * Returns: %TRUE on success, %FALSE otherwise - * Since: UNRELEASED +/* Deallocate a tmpdir, closing the fd and (recursively) deleting the path. This + * is normally called by default by the autocleanup attribute, but you can also + * invoke this directly. */ -gboolean -glnx_mkdtempat_open_in_system (gchar *tmpl, - int mode, - int *out_dfd, - GError **error) +void +glnx_tmpdir_clear (GLnxTmpDir *tmpd) { - glnx_fd_close int tmp_dfd = -1; - - *out_dfd = -1; - - if (!glnx_opendirat (-1, g_get_tmp_dir (), TRUE, &tmp_dfd, error)) - return FALSE; - - return glnx_mkdtempat_open (tmp_dfd, tmpl, mode, out_dfd, error); + /* Support being passed NULL so we work nicely in a GPtrArray */ + if (!tmpd) + return; + if (!tmpd->initialized) + return; + g_assert_cmpint (tmpd->fd, !=, -1); + (void) close (tmpd->fd); + g_assert (tmpd->path); + g_assert_cmpint (tmpd->src_dfd, !=, -1); + (void) glnx_shutil_rm_rf_at (tmpd->src_dfd, tmpd->path, NULL, NULL); + g_free (tmpd->path); + tmpd->initialized = FALSE; } diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 8e582fc1..5e362fab 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -113,20 +113,19 @@ glnx_ensure_dir (int dfd, return TRUE; } -gboolean glnx_mkdtempat (int dfd, - gchar *tmpl, - int mode, - GError **error); +typedef struct { + gboolean initialized; + int src_dfd; + int fd; + char *path; +} GLnxTmpDir; +void glnx_tmpdir_clear (GLnxTmpDir *tmpf); +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpDir, glnx_tmpdir_clear) -gboolean glnx_mkdtempat_open (int dfd, - gchar *tmpl, - int mode, - int *out_dfd, - GError **error); +gboolean glnx_mkdtempat (int dfd, const char *tmpl, int mode, + GLnxTmpDir *out_tmpdir, GError **error); -gboolean glnx_mkdtempat_open_in_system (gchar *tmpl, - int mode, - int *out_dfd, - GError **error); +gboolean glnx_mkdtemp (const char *tmpl, int mode, + GLnxTmpDir *out_tmpdir, GError **error); G_END_DECLS diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 37c982d7..1f94ce2e 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -224,18 +224,17 @@ test_xattr_races (void) g_autoptr(GError) local_error = NULL; GError **error = &local_error; glnx_fd_close int dfd = -1; - g_autofree char *tmpdir = g_strdup_printf ("%s/libglnx-xattrs-XXXXXX", - getenv ("TMPDIR") ?: "/var/tmp"); + g_auto(GLnxTmpDir) tmpdir = { 0, }; + g_autofree char *tmpdir_path = g_strdup_printf ("%s/libglnx-xattrs-XXXXXX", + getenv ("TMPDIR") ?: "/var/tmp"); guint nread = 0; - if (!glnx_mkdtempat (AT_FDCWD, tmpdir, 0700, error)) - goto out; - - if (!glnx_opendirat (AT_FDCWD, tmpdir, TRUE, &dfd, error)) + if (!glnx_mkdtempat (AT_FDCWD, tmpdir_path, 0700, + &tmpdir, error)) goto out; /* Support people building/testing on tmpfs https://github.com/flatpak/flatpak/issues/686 */ - if (fsetxattr (dfd, "user.test", "novalue", strlen ("novalue"), 0) < 0) + if (fsetxattr (tmpdir.fd, "user.test", "novalue", strlen ("novalue"), 0) < 0) { if (errno == EOPNOTSUPP) { @@ -252,7 +251,7 @@ test_xattr_races (void) for (guint i = 0; i < nprocs; i++) { struct XattrWorker *worker = &wdata[i]; - worker->dfd = dfd; + worker->dfd = tmpdir.fd; worker->is_writer = i % 2 == 0; threads[i] = g_thread_new (NULL, xattr_thread, worker); } @@ -267,8 +266,6 @@ test_xattr_races (void) g_print ("Read %u xattrs race free!\n", nread); - (void) glnx_shutil_rm_rf_at (AT_FDCWD, tmpdir, NULL, NULL); - out: g_assert_no_error (local_error); } From 47d816329370ca315d4ffec9872690b1fd4c27c9 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 25 Aug 2017 10:27:55 -0400 Subject: [PATCH 155/280] test-libglnx-xattrs.c: appease -Wunused-variable --- tests/test-libglnx-xattrs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 1f94ce2e..0076b712 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -223,7 +223,6 @@ test_xattr_races (void) GThread *threads[nprocs]; g_autoptr(GError) local_error = NULL; GError **error = &local_error; - glnx_fd_close int dfd = -1; g_auto(GLnxTmpDir) tmpdir = { 0, }; g_autofree char *tmpdir_path = g_strdup_printf ("%s/libglnx-xattrs-XXXXXX", getenv ("TMPDIR") ?: "/var/tmp"); From 627d4e2f15572e853279d4bb9511ef2618bef6b5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 6 Sep 2017 14:06:37 -0400 Subject: [PATCH 156/280] fdio: Add glnx_fstatat_allow_noent() This is a very common pattern in both ostree/rpm-ostree. Make a better API for this. I thought a lot about simply zeroing out `struct stat` but that feels dangerous; none of the values have seem obviously `cannot be zero`. --- glnx-fdio.h | 38 ++++++++++++++++++++++++++++++++++++++ tests/test-libglnx-fdio.c | 22 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index 4ee24afa..518135c5 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -295,6 +295,44 @@ glnx_fstatat (int dfd, return TRUE; } +/** + * glnx_fstatat_allow_noent: + * @dfd: Directory FD to stat beneath + * @path: Path to stat beneath @dfd + * @buf: (out caller-allocates): Return location for stat details + * @flags: Flags to pass to fstatat() + * @error: Return location for a #GError, or %NULL + * + * Like glnx_fstatat(), but handles `ENOENT` in a non-error way. Instead, + * on success `errno` will be zero, otherwise it will be preserved. Hence + * you can test `if (errno == 0)` to conditionalize on the file existing, + * or `if (errno == ENOENT)` for non-existence. + * + * Returns: %TRUE on success, %FALSE otherwise (errno is preserved) + * Since: UNRELEASED + */ +static inline gboolean +glnx_fstatat_allow_noent (int dfd, + const char *path, + struct stat *out_buf, + int flags, + GError **error) +{ + if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf, flags)) != 0) + { + if (errno != ENOENT) + { + int errsv = errno; + (void) glnx_throw_errno_prefix (error, "fstatat(%s)", path); + errno = errsv; + return FALSE; + } + } + else + errno = 0; + return TRUE; +} + /** * glnx_renameat: * diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 16b66926..4b81a957 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -189,6 +189,27 @@ test_stdio_file (void) g_assert_no_error (local_error); } +static void +test_fstatat (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + struct stat stbuf = { 0, }; + + if (!glnx_fstatat_allow_noent (AT_FDCWD, ".", &stbuf, 0, error)) + goto out; + g_assert_cmpint (errno, ==, 0); + g_assert_no_error (local_error); + g_assert (S_ISDIR (stbuf.st_mode)); + if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchfile", &stbuf, 0, error)) + goto out; + g_assert_cmpint (errno, ==, ENOENT); + g_assert_no_error (local_error); + + out: + g_assert_no_error (local_error); +} + int main (int argc, char **argv) { int ret; @@ -199,6 +220,7 @@ int main (int argc, char **argv) g_test_add_func ("/stdio-file", test_stdio_file); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); + g_test_add_func ("/fstat", test_fstatat); ret = g_test_run(); From 806bb46e054d2c960f129503dddf724383700899 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 11 Sep 2017 16:58:53 -0400 Subject: [PATCH 157/280] fdio: Use O_EXCL for anonymous tmpfiles I noticed while reading the manpage for `linkat()` that `O_TMPFILE` supports `O_EXCL` to mean exactly what we're doing with the anonymous tmpfile API. Change the code to start using it; this required refactoring the internals since we had a check to be sure the caller wasn't passing `O_EXCL` for the non-anonymous path which we want to keep. Presumably the storage system could do smarter things if it knows a file will always be anonymous, e.g. it doesn't need to journal its data. --- glnx-fdio.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index dc8459d9..e8e2167f 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -180,20 +180,11 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf) tmpf->initialized = FALSE; } -/* Allocate a temporary file, using Linux O_TMPFILE if available. The file mode - * will be 0600. - * - * The result will be stored in @out_tmpf, which is caller allocated - * so you can store it on the stack in common scenarios. - * - * The directory fd @dfd must live at least as long as the output @out_tmpf. - */ -gboolean -glnx_open_tmpfile_linkable_at (int dfd, - const char *subpath, - int flags, - GLnxTmpfile *out_tmpf, - GError **error) +static gboolean +open_tmpfile_core (int dfd, const char *subpath, + int flags, + GLnxTmpfile *out_tmpf, + GError **error) { const guint mode = 0600; glnx_fd_close int fd = -1; @@ -201,9 +192,6 @@ glnx_open_tmpfile_linkable_at (int dfd, dfd = glnx_dirfd_canonicalize (dfd); - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE); - /* Creates a temporary file, that shall be renamed to "target" * later. If possible, this uses O_TMPFILE – in which case * "ret_path" will be returned as NULL. If not possible a the @@ -260,6 +248,29 @@ glnx_open_tmpfile_linkable_at (int dfd, return FALSE; } +/* Allocate a temporary file, using Linux O_TMPFILE if available. The file mode + * will be 0600. + * + * The result will be stored in @out_tmpf, which is caller allocated + * so you can store it on the stack in common scenarios. + * + * The directory fd @dfd must live at least as long as the output @out_tmpf. + */ +gboolean +glnx_open_tmpfile_linkable_at (int dfd, + const char *subpath, + int flags, + GLnxTmpfile *out_tmpf, + GError **error) +{ + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE; + * it's used for glnx_open_anonymous_tmpfile(). + */ + g_return_val_if_fail ((flags & O_EXCL) == 0, FALSE); + + return open_tmpfile_core (dfd, subpath, flags, out_tmpf, error); +} + /* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. * Useful for true temporary storage. The fd will be allocated in /var/tmp to * ensure maximum storage space. @@ -269,7 +280,8 @@ glnx_open_anonymous_tmpfile (int flags, GLnxTmpfile *out_tmpf, GError **error) { - if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, "/var/tmp", flags, out_tmpf, error)) + /* Add in O_EXCL */ + if (!open_tmpfile_core (AT_FDCWD, "/var/tmp", flags | O_EXCL, out_tmpf, error)) return FALSE; if (out_tmpf->path) { From 9d995a362009ae01bca14c781e14d5623ad27cd6 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 11 Sep 2017 17:17:12 -0400 Subject: [PATCH 158/280] fdio: Support taking ownership of tmpfile fd While reading a strace I noticed a double close in the tests; this was because we were missing an assignment to `-1` in the tests. However, let's make supporting this clearer by explicitly supporting the fd being `-1` while still setting the `initialized` variable to `FALSE`. We also add the `EBADF` assertion checking. --- glnx-fdio.c | 8 +++++--- tests/test-libglnx-fdio.c | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index e8e2167f..0046807a 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -168,9 +168,11 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf) return; if (!tmpf->initialized) return; - if (tmpf->fd == -1) - return; - (void) close (tmpf->fd); + if (tmpf->fd != -1) + { + if (close (tmpf->fd) < 0) + g_assert (errno != EBADF); + } /* If ->path is set, we're likely aborting due to an error. Clean it up */ if (tmpf->path) { diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 4b81a957..36ded80c 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -171,6 +171,7 @@ test_stdio_file (void) if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error)) goto out; f = fdopen (tmpf.fd, "w"); + tmpf.fd = -1; /* Ownership was transferred via fdopen() */ if (!f) { (void)glnx_throw_errno_prefix (error, "fdopen"); From 673f48f6ca03131a32c8b5a8c2975f3995802423 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 11 Sep 2017 16:51:13 -0400 Subject: [PATCH 159/280] fdio: Use O_TMPFILE + rename-overwrite for regfile copies I was working on rpm-ostree unified core, and hit the fact that `glnx_file_copy_at()` had the same bug with `fsetxattr()` and files whose mode is <= `0400` (e.g. `000` in the case of `/etc/shadow`) that libostree did a while ago. Basically, Linux currently allows `write()` on non-writable open files but not `fsetxattr()`. This situation is masked for privileged (i.e. `CAP_DAC_OVERRIDE`) code. Looking at this, I think it's cleaner to convert to `O_TMPFILE` here, since that code already handles setting the tmpfile to mode `0600`. Now, this *is* a behavior change in the corner case of existing files which are symbolic links. Previously we'd do an `open(O_TRUNC)` which would follow the link. But in the big picture, I think the use cases for `open(O_TRUNC)` are really rare - I audited all callers of this in ostree/rpm-ostree/flatpak, and all of them will be fine with this behavior change. For example, the ostree `/etc` merge code already explicitly unlinks the target beforehand. Other cases like supporting `repo/pubring.gpg` in an ostree repo being a symlink...eh, just no. Making this change allows us to convert to new style, and brings all of the general benefits of using `O_TMPFILE` too. --- glnx-fdio.c | 132 ++++++++++++++++---------------------- tests/test-libglnx-fdio.c | 62 ++++++++++++++++++ 2 files changed, 118 insertions(+), 76 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 0046807a..8093836c 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -872,11 +872,15 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) * @cancellable: cancellable * @error: Error * - * Perform a full copy of the regular file or - * symbolic link from @src_subpath to @dest_subpath. + * Perform a full copy of the regular file or symbolic link from @src_subpath to + * @dest_subpath; if @src_subpath is anything other than a regular file or + * symbolic link, an error will be returned. * - * If @src_subpath is anything other than a regular - * file or symbolic link, an error will be returned. + * If the source is a regular file and the destination exists as a symbolic + * link, the symbolic link will not be followed; rather the link itself will be + * replaced. Related to this: for regular files, when `GLNX_FILE_COPY_OVERWRITE` + * is specified, this function always uses `O_TMPFILE` (if available) and does a + * rename-into-place rather than `open(O_TRUNC)`. */ gboolean glnx_file_copy_at (int src_dfd, @@ -888,31 +892,23 @@ glnx_file_copy_at (int src_dfd, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - int r; - int dest_open_flags; - struct timespec ts[2]; - glnx_fd_close int src_fd = -1; - glnx_fd_close int dest_fd = -1; - struct stat local_stbuf; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - goto out; - + /* Canonicalize dfds */ src_dfd = glnx_dirfd_canonicalize (src_dfd); dest_dfd = glnx_dirfd_canonicalize (dest_dfd); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + /* Automatically do stat() if no stat buffer was supplied */ + struct stat local_stbuf; if (!src_stbuf) { - if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + if (!glnx_fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; src_stbuf = &local_stbuf; } + /* For symlinks, defer entirely to copy_symlink_at() */ if (S_ISLNK (src_stbuf->st_mode)) { return copy_symlink_at (src_dfd, src_subpath, src_stbuf, @@ -924,47 +920,26 @@ glnx_file_copy_at (int src_dfd, { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cannot copy non-regular/non-symlink file: %s", src_subpath); - goto out; + return FALSE; } - if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) - goto out; - - dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY; - if (!(copyflags & GLNX_FILE_COPY_OVERWRITE)) - dest_open_flags |= O_EXCL; - else - dest_open_flags |= O_TRUNC; - - dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode)); - if (dest_fd == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + /* Regular file path below here */ - r = glnx_regfile_copy_bytes (src_fd, dest_fd, (off_t) -1); - if (r < 0) - { - glnx_set_error_from_errno (error); - goto out; - } + glnx_fd_close int src_fd = -1; + if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) + return FALSE; - if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + /* Open a tmpfile for dest */ + g_auto(GLnxTmpfile) tmp_dest = { 0, }; + if (!glnx_open_tmpfile_linkable_at (dest_dfd, ".", O_WRONLY | O_CLOEXEC, + &tmp_dest, error)) + return FALSE; - if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0) - { - glnx_set_error_from_errno (error); - goto out; - } + if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0) + return glnx_throw_errno_prefix (error, "regfile copy"); - ts[0] = src_stbuf->st_atim; - ts[1] = src_stbuf->st_mtim; - (void) futimens (dest_fd, ts); + if (fchown (tmp_dest.fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) + return glnx_throw_errno_prefix (error, "fchown"); if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) { @@ -972,35 +947,40 @@ glnx_file_copy_at (int src_dfd, if (!glnx_fd_get_all_xattrs (src_fd, &xattrs, cancellable, error)) - goto out; + return FALSE; - if (!glnx_fd_set_all_xattrs (dest_fd, xattrs, + if (!glnx_fd_set_all_xattrs (tmp_dest.fd, xattrs, cancellable, error)) - goto out; + return FALSE; } + /* Always chmod after setting xattrs, in case the file has mode 0400 or less, + * like /etc/shadow. Linux currently allows write() on non-writable open files + * but not fsetxattr(). + */ + if (fchmod (tmp_dest.fd, src_stbuf->st_mode & 07777) != 0) + return glnx_throw_errno_prefix (error, "fchmod"); + + struct timespec ts[2]; + ts[0] = src_stbuf->st_atim; + ts[1] = src_stbuf->st_mtim; + (void) futimens (tmp_dest.fd, ts); + if (copyflags & GLNX_FILE_COPY_DATASYNC) { - if (fdatasync (dest_fd) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - } - - r = close (dest_fd); - dest_fd = -1; - if (r < 0) - { - glnx_set_error_from_errno (error); - goto out; + if (fdatasync (tmp_dest.fd) < 0) + return glnx_throw_errno_prefix (error, "fdatasync"); } - ret = TRUE; - out: - if (!ret) - (void) unlinkat (dest_dfd, dest_subpath, 0); - return ret; + const GLnxLinkTmpfileReplaceMode replacemode = + (copyflags & GLNX_FILE_COPY_OVERWRITE) ? + GLNX_LINK_TMPFILE_REPLACE : + GLNX_LINK_TMPFILE_NOREPLACE; + + if (!glnx_link_tmpfile_at (&tmp_dest, replacemode, dest_dfd, dest_subpath, error)) + return FALSE; + + return TRUE; } /** diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 36ded80c..4047df64 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -211,6 +211,67 @@ test_fstatat (void) g_assert_no_error (local_error); } +static void +test_filecopy (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + g_auto(GLnxTmpfile) tmpf = { 0, }; + const char foo[] = "foo"; + + if (!glnx_file_replace_contents_at (AT_FDCWD, foo, (guint8*)foo, sizeof (foo), + GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) + goto out; + + if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", + GLNX_FILE_COPY_NOXATTRS, NULL, error)) + goto out; + + if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", + GLNX_FILE_COPY_NOXATTRS, NULL, error)) + g_assert_not_reached (); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_clear_error (&local_error); + + if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", + GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_OVERWRITE, + NULL, error)) + goto out; + + if (symlinkat ("nosuchtarget", AT_FDCWD, "link") < 0) + { + glnx_throw_errno_prefix (error, "symlinkat"); + goto out; + } + + /* Shouldn't be able to overwrite a symlink without GLNX_FILE_COPY_OVERWRITE */ + if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "link", + GLNX_FILE_COPY_NOXATTRS, + NULL, error)) + g_assert_not_reached (); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_clear_error (&local_error); + + /* Test overwriting symlink */ + if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "link", + GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_OVERWRITE, + NULL, error)) + goto out; + + struct stat stbuf; + if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchtarget", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + goto out; + g_assert_cmpint (errno, ==, ENOENT); + g_assert_no_error (local_error); + + if (!glnx_fstatat (AT_FDCWD, "link", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + goto out; + g_assert (S_ISREG (stbuf.st_mode)); + + out: + g_assert_no_error (local_error); +} + int main (int argc, char **argv) { int ret; @@ -219,6 +280,7 @@ int main (int argc, char **argv) g_test_add_func ("/tmpfile", test_tmpfile); g_test_add_func ("/stdio-file", test_stdio_file); + g_test_add_func ("/filecopy", test_filecopy); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); g_test_add_func ("/fstat", test_fstatat); From b59bb2be7cf092f82932c8671d24ef1362aac364 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Sep 2017 11:09:11 -0400 Subject: [PATCH 160/280] fdio: Add gtk-doc for stbuf parameter of glnx_file_copy_at() Spotted in https://github.com/GNOME/libglnx/pull/80/commits/ba5e1cf9f58770ba879e9fb6ac337ccec9d0a10c --- glnx-fdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 8093836c..a257c056 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -866,6 +866,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) * glnx_file_copy_at: * @src_dfd: Source directory fd * @src_subpath: Subpath relative to @src_dfd + * @src_stbuf: (allow-none): Optional stat buffer for source; if a stat() has already been done * @dest_dfd: Target directory fd * @dest_subpath: Destination name * @copyflags: Flags From 0428fd87ff1493699359cde20909719ee33f4769 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Sep 2017 14:53:35 -0400 Subject: [PATCH 161/280] dirfd: Extend tmpdir API to support optional cleaning We have a use case in libostree's staging dirs where we try to reuse them across multiple ostree txns, but we want the fd-relative bits here. Extend the tmpdir API to make deletion optional. While here, also extend the API to support checking for errors when deleting for projects like libostree that want to do so consistently. Also while here, add a change to set the fd to `-1` after clearing to be extra defensive. --- glnx-dirfd.c | 61 +++++++++++++++++++++++++++++++++++++++++----------- glnx-dirfd.h | 10 +++++++-- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index b745e280..cbc31f9f 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -371,25 +371,62 @@ glnx_mkdtemp (const gchar *tmpl, out_tmpdir, error); } -/* Deallocate a tmpdir, closing the fd and (recursively) deleting the path. This - * is normally called by default by the autocleanup attribute, but you can also - * invoke this directly. - */ -void -glnx_tmpdir_clear (GLnxTmpDir *tmpd) +static gboolean +_glnx_tmpdir_free (GLnxTmpDir *tmpd, + gboolean delete_dir, + GCancellable *cancellable, + GError **error) { /* Support being passed NULL so we work nicely in a GPtrArray */ - if (!tmpd) - return; - if (!tmpd->initialized) - return; + if (!(tmpd && tmpd->initialized)) + return TRUE; g_assert_cmpint (tmpd->fd, !=, -1); (void) close (tmpd->fd); + tmpd->fd = -1; g_assert (tmpd->path); g_assert_cmpint (tmpd->src_dfd, !=, -1); - (void) glnx_shutil_rm_rf_at (tmpd->src_dfd, tmpd->path, NULL, NULL); - g_free (tmpd->path); + g_autofree char *path = tmpd->path; /* Take ownership */ tmpd->initialized = FALSE; + if (delete_dir) + { + if (!glnx_shutil_rm_rf_at (tmpd->src_dfd, path, cancellable, error)) + return FALSE; + } + return TRUE; } +/** + * glnx_tmpdir_delete: + * @tmpf: Temporary dir + * @cancellable: Cancellable + * @error: Error + * + * Deallocate a tmpdir, closing the fd and recursively deleting the path. This + * is normally called indirectly via glnx_tmpdir_cleanup() by the autocleanup + * attribute, but you can also invoke this directly. + * + * If an error occurs while deleting the filesystem path, @tmpf will still have + * been deallocated and should not be reused. + * + * See also `glnx_tmpdir_unset` to avoid deleting the path. + */ +gboolean +glnx_tmpdir_delete (GLnxTmpDir *tmpf, GCancellable *cancellable, GError **error) +{ + return _glnx_tmpdir_free (tmpf, TRUE, cancellable, error); +} +/** + * glnx_tmpdir_unset: + * @tmpf: Temporary dir + * @cancellable: Cancellable + * @error: Error + * + * Deallocate a tmpdir, but do not delete the filesystem path. See also + * `glnx_tmpdir_delete()`. + */ +void +glnx_tmpdir_unset (GLnxTmpDir *tmpf) +{ + (void) _glnx_tmpdir_free (tmpf, FALSE, NULL, NULL); +} diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 5e362fab..11d791e8 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -119,8 +119,14 @@ typedef struct { int fd; char *path; } GLnxTmpDir; -void glnx_tmpdir_clear (GLnxTmpDir *tmpf); -G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpDir, glnx_tmpdir_clear) +gboolean glnx_tmpdir_delete (GLnxTmpDir *tmpf, GCancellable *cancellable, GError **error); +void glnx_tmpdir_unset (GLnxTmpDir *tmpf); +static inline void +glnx_tmpdir_cleanup (GLnxTmpDir *tmpf) +{ + (void)glnx_tmpdir_delete (tmpf, NULL, NULL); +} +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxTmpDir, glnx_tmpdir_cleanup) gboolean glnx_mkdtempat (int dfd, const char *tmpl, int mode, GLnxTmpDir *out_tmpdir, GError **error); From 667d8aa7217eefcbc1c701b8f96ecf8cbeff0f8a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Sep 2017 16:00:47 -0400 Subject: [PATCH 162/280] tree-wide: Use our own syscall wrappers or error prefixing Followup to similar commits in the ostree stack recently. --- glnx-dirfd.c | 5 +++-- glnx-fdio.c | 26 ++++++++++++-------------- glnx-lockfile.c | 7 +++---- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index cbc31f9f..ea12c8f9 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -204,8 +205,8 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, if (ret_dent->d_type == DT_UNKNOWN) { struct stat stbuf; - if (TEMP_FAILURE_RETRY (fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) - return glnx_throw_errno (error); + if (!glnx_fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; ret_dent->d_type = IFTODT (stbuf.st_mode); } } diff --git a/glnx-fdio.c b/glnx-fdio.c index a257c056..7113aebc 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -112,7 +112,7 @@ rename_file_noreplace_at (int olddirfd, const char *oldpath, return TRUE; } else - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "renameat"); } return TRUE; } @@ -442,8 +442,8 @@ glnx_fd_readall_malloc (int fd, const guint maxreadlen = 4096; struct stat stbuf; - if (TEMP_FAILURE_RETRY (fstat (fd, &stbuf)) < 0) - return glnx_null_throw_errno (error); + if (!glnx_fstat (fd, &stbuf, error)) + return FALSE; gsize buf_allocated; if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0) @@ -611,7 +611,7 @@ glnx_readlinkat_malloc (int dfd, c = g_malloc (l); n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); if (n < 0) - return glnx_null_throw_errno (error); + return glnx_null_throw_errno_prefix (error, "readlinkat"); if ((size_t) n < l-1) { @@ -658,7 +658,7 @@ copy_symlink_at (int src_dfd, if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath, src_stbuf->st_uid, src_stbuf->st_gid, AT_SYMLINK_NOFOLLOW)) != 0) - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "fchownat"); return TRUE; } @@ -1066,19 +1066,17 @@ glnx_file_replace_contents_with_perms_at (int dfd, return FALSE; if (glnx_loop_write (tmpf.fd, buf, len) < 0) - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "write"); if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) { struct stat stbuf; gboolean do_sync; - if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) - { - if (errno != ENOENT) - return glnx_throw_errno (error); - do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; - } + if (!glnx_fstatat_allow_noent (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + if (errno == ENOENT) + do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; else do_sync = TRUE; @@ -1092,11 +1090,11 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (uid != (uid_t) -1) { if (fchown (tmpf.fd, uid, gid) != 0) - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "fchown"); } if (fchmod (tmpf.fd, mode) != 0) - return glnx_throw_errno (error); + return glnx_throw_errno_prefix (error, "fchmod"); if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE, dfd, subpath, error)) diff --git a/glnx-lockfile.c b/glnx-lockfile.c index c1cfc6b2..48f1ea75 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -105,7 +105,7 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc r = flock(fd, operation); if (r < 0) - return glnx_throw_errno(error); + return glnx_throw_errno_prefix (error, "flock"); } /* If we acquired the lock, let's check if the file @@ -114,9 +114,8 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc * it. In such a case our acquired lock is worthless, * hence try again. */ - r = fstat(fd, &st); - if (r < 0) - return glnx_throw_errno(error); + if (!glnx_fstat (fd, &st, error)) + return FALSE; if (st.st_nlink > 0) break; From c2bcca04bac7e6ba99ba4cc1d17e99c5ed2b12cf Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Sep 2017 15:27:36 -0400 Subject: [PATCH 163/280] tests: Add macro for auto-error checking Having our tests forced into a `goto out` style is seriously annoying since we can't write tests like we write production code. Add a macro that checks for the error being NULL. This doesn't fully solve the problem since the test functions are still forced into `void` returns; at some point I may extend GLib to have `g_test_add_err_func()`. --- tests/libglnx-testlib.h | 34 ++++++++++ tests/test-libglnx-fdio.c | 135 +++++++++++--------------------------- 2 files changed, 74 insertions(+), 95 deletions(-) create mode 100644 tests/libglnx-testlib.h diff --git a/tests/libglnx-testlib.h b/tests/libglnx-testlib.h new file mode 100644 index 00000000..ee750e49 --- /dev/null +++ b/tests/libglnx-testlib.h @@ -0,0 +1,34 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Red Hat, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +typedef GError _GLnxTestAutoError; +static inline void +_glnx_test_auto_error_cleanup (_GLnxTestAutoError *autoerror) +{ + g_assert_no_error (autoerror); + /* We could add a clear call here, but no point...we'll have aborted */ +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_GLnxTestAutoError, _glnx_test_auto_error_cleanup); + +#define _GLNX_TEST_DECLARE_ERROR(local_error, error) \ + g_autoptr(_GLnxTestAutoError) local_error = NULL; \ + GError **error = &local_error diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 4047df64..9ec9be72 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -26,6 +26,8 @@ #include #include +#include "libglnx-testlib.h" + static gboolean renameat_test_setup (int *out_srcfd, int *out_destfd, GError **error) @@ -59,14 +61,13 @@ renameat_test_setup (int *out_srcfd, int *out_destfd, static void test_renameat2_noreplace (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; + _GLNX_TEST_DECLARE_ERROR(local_error, error); glnx_fd_close int srcfd = -1; glnx_fd_close int destfd = -1; struct stat stbuf; if (!renameat_test_setup (&srcfd, &destfd, error)) - goto out; + return; if (glnx_renameat2_noreplace (srcfd, "foo", destfd, "bar") == 0) g_assert_not_reached (); @@ -76,156 +77,106 @@ test_renameat2_noreplace (void) } if (glnx_renameat2_noreplace (srcfd, "foo", destfd, "baz") < 0) - { - glnx_set_error_from_errno (error); - goto out; - } + return (void)glnx_throw_errno_prefix (error, "renameat"); if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW, error)) - goto out; + return; if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) == 0) g_assert_not_reached (); else g_assert_cmpint (errno, ==, ENOENT); - - out: - g_assert_no_error (local_error); } static void test_renameat2_exchange (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; + _GLNX_TEST_DECLARE_ERROR(local_error, error); + glnx_fd_close int srcfd = -1; glnx_fd_close int destfd = -1; - struct stat stbuf; - if (!renameat_test_setup (&srcfd, &destfd, error)) - goto out; + return; if (glnx_renameat2_exchange (AT_FDCWD, "srcdir", AT_FDCWD, "destdir") < 0) - { - glnx_set_error_from_errno (error); - goto out; - } + return (void)glnx_throw_errno_prefix (error, "renameat"); /* Ensure the dir fds are the same */ - if (fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - if (fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } + struct stat stbuf; + if (!glnx_fstatat (srcfd, "foo", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return; + if (!glnx_fstatat (destfd, "bar", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return; /* But the dirs should be swapped */ - if (fstatat (AT_FDCWD, "destdir/foo", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - if (fstatat (AT_FDCWD, "srcdir/bar", &stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - glnx_set_error_from_errno (error); - goto out; - } - - out: - g_assert_no_error (local_error); + if (!glnx_fstatat (AT_FDCWD, "destdir/foo", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return; + if (!glnx_fstatat (AT_FDCWD, "srcdir/bar", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return; } static void test_tmpfile (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; - g_auto(GLnxTmpfile) tmpf = { 0, }; + _GLNX_TEST_DECLARE_ERROR(local_error, error); + g_auto(GLnxTmpfile) tmpf = { 0, }; if (!glnx_open_tmpfile_linkable_at (AT_FDCWD, ".", O_WRONLY|O_CLOEXEC, &tmpf, error)) - goto out; - + return; if (glnx_loop_write (tmpf.fd, "foo", strlen ("foo")) < 0) - { - (void)glnx_throw_errno_prefix (error, "write"); - goto out; - } - + return (void)glnx_throw_errno_prefix (error, "write"); if (glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_NOREPLACE, AT_FDCWD, "foo", error)) - goto out; - - out: - g_assert_no_error (local_error); + return; } static void test_stdio_file (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; + _GLNX_TEST_DECLARE_ERROR(local_error, error); g_auto(GLnxTmpfile) tmpf = { 0, }; g_autoptr(FILE) f = NULL; if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error)) - goto out; + return; f = fdopen (tmpf.fd, "w"); tmpf.fd = -1; /* Ownership was transferred via fdopen() */ if (!f) - { - (void)glnx_throw_errno_prefix (error, "fdopen"); - goto out; - } - + return (void)glnx_throw_errno_prefix (error, "fdopen"); if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello")) - { - (void)glnx_throw_errno_prefix (error, "fwrite"); - goto out; - } + return (void)glnx_throw_errno_prefix (error, "fwrite"); if (!glnx_stdio_file_flush (f, error)) - goto out; - - out: - g_assert_no_error (local_error); + return; } static void test_fstatat (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; + _GLNX_TEST_DECLARE_ERROR(local_error, error); struct stat stbuf = { 0, }; if (!glnx_fstatat_allow_noent (AT_FDCWD, ".", &stbuf, 0, error)) - goto out; + return; g_assert_cmpint (errno, ==, 0); g_assert_no_error (local_error); g_assert (S_ISDIR (stbuf.st_mode)); if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchfile", &stbuf, 0, error)) - goto out; + return; g_assert_cmpint (errno, ==, ENOENT); g_assert_no_error (local_error); - - out: - g_assert_no_error (local_error); } static void test_filecopy (void) { - g_autoptr(GError) local_error = NULL; - GError **error = &local_error; + _GLNX_TEST_DECLARE_ERROR(local_error, error); g_auto(GLnxTmpfile) tmpf = { 0, }; const char foo[] = "foo"; if (!glnx_file_replace_contents_at (AT_FDCWD, foo, (guint8*)foo, sizeof (foo), GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) - goto out; + return; if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) - goto out; + return; if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) @@ -236,13 +187,10 @@ test_filecopy (void) if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_OVERWRITE, NULL, error)) - goto out; + return; if (symlinkat ("nosuchtarget", AT_FDCWD, "link") < 0) - { - glnx_throw_errno_prefix (error, "symlinkat"); - goto out; - } + return (void) glnx_throw_errno_prefix (error, "symlinkat"); /* Shouldn't be able to overwrite a symlink without GLNX_FILE_COPY_OVERWRITE */ if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "link", @@ -256,20 +204,17 @@ test_filecopy (void) if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "link", GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_OVERWRITE, NULL, error)) - goto out; + return; struct stat stbuf; if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchtarget", &stbuf, AT_SYMLINK_NOFOLLOW, error)) - goto out; + return; g_assert_cmpint (errno, ==, ENOENT); g_assert_no_error (local_error); if (!glnx_fstatat (AT_FDCWD, "link", &stbuf, AT_SYMLINK_NOFOLLOW, error)) - goto out; + return; g_assert (S_ISREG (stbuf.st_mode)); - - out: - g_assert_no_error (local_error); } int main (int argc, char **argv) From e5856ca2939dca0589a836e3108dd3f9759e28fa Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 17 Sep 2017 09:04:56 -0400 Subject: [PATCH 164/280] build-sys: Fix make dist The libostree Travis builds use make dist. --- Makefile-libglnx.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 6df05ea1..de6e49be 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -46,6 +46,7 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-shutil.h \ $(libglnx_srcpath)/glnx-shutil.c \ $(libglnx_srcpath)/libglnx.h \ + $(libglnx_srcpath)/tests/libglnx-testlib.h \ $(NULL) libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) From 56e7e728ab64b3798ff2867492d416219f3effe5 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 21 Sep 2017 20:20:47 +0100 Subject: [PATCH 165/280] dirfd: Fix typo in documentation for glnx_ensure_dir() Signed-off-by: Philip Withnall --- glnx-dirfd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 11d791e8..0046ac8e 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -92,7 +92,7 @@ void glnx_gen_temp_name (gchar *tmpl); * @mode: Mode * @error: Return location for a #GError, or %NULL * - * Wrapper around mkdirat() which ignores adds #GError support, ensures that + * Wrapper around mkdirat() which adds #GError support, ensures that * it retries on %EINTR, and also ignores `EEXIST`. * * See also `glnx_shutil_mkdir_p_at()` for recursive handling. From 5ee2f1be7a10a3644168d7f9e6281d4c5bcabe87 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 22 Sep 2017 11:34:14 -0400 Subject: [PATCH 166/280] fdio: Open target dirname for glnx_file_copy_at() Particularly if `AT_FDCWD` is used, we need to open in the target dir, otherwise we can get `EXDEV` when trying to do the final link. (Theoretically we can cross a mountpoint even with fd-relative though this is a lot less likely) --- glnx-fdio.c | 13 +++++++++---- tests/test-libglnx-fdio.c | 11 ++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 7113aebc..53f82e27 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -930,11 +930,16 @@ glnx_file_copy_at (int src_dfd, if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) return FALSE; - /* Open a tmpfile for dest */ + /* Open a tmpfile for dest. Particularly for AT_FDCWD calls, we really want to + * open in the target directory, otherwise we may not be able to link. + */ g_auto(GLnxTmpfile) tmp_dest = { 0, }; - if (!glnx_open_tmpfile_linkable_at (dest_dfd, ".", O_WRONLY | O_CLOEXEC, - &tmp_dest, error)) - return FALSE; + { char *dnbuf = strdupa (dest_subpath); + const char *dn = dirname (dnbuf); + if (!glnx_open_tmpfile_linkable_at (dest_dfd, dn, O_WRONLY | O_CLOEXEC, + &tmp_dest, error)) + return FALSE; + } if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0) return glnx_throw_errno_prefix (error, "regfile copy"); diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 9ec9be72..bf973b9a 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -169,14 +169,24 @@ test_filecopy (void) _GLNX_TEST_DECLARE_ERROR(local_error, error); g_auto(GLnxTmpfile) tmpf = { 0, }; const char foo[] = "foo"; + struct stat stbuf; + + if (!glnx_ensure_dir (AT_FDCWD, "subdir", 0755, error)) + return; if (!glnx_file_replace_contents_at (AT_FDCWD, foo, (guint8*)foo, sizeof (foo), GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) return; + /* Copy it into both the same dir and a subdir */ if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) return; + if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "subdir/bar", + GLNX_FILE_COPY_NOXATTRS, NULL, error)) + return; + if (!glnx_fstatat (AT_FDCWD, "subdir/bar", &stbuf, 0, error)) + return; if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) @@ -206,7 +216,6 @@ test_filecopy (void) NULL, error)) return; - struct stat stbuf; if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchtarget", &stbuf, AT_SYMLINK_NOFOLLOW, error)) return; g_assert_cmpint (errno, ==, ENOENT); From 292cfc807e1662d5f6fe16f4d372e97973956955 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Mon, 25 Sep 2017 15:38:25 +0000 Subject: [PATCH 167/280] macros: use size_t for glnx_strjoina len This was in my workspace for a while. `strlen` returns a `size_t` and `alloca` expects a `size_t`. --- glnx-macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-macros.h b/glnx-macros.h index 1d4e1753..6d8aca93 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -38,7 +38,7 @@ G_BEGIN_DECLS ({ \ const char *_appendees_[] = { a, __VA_ARGS__ }; \ char *_d_, *_p_; \ - int _len_ = 0; \ + size_t _len_ = 0; \ unsigned _i_; \ for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ From 32a4293101ffe6f7dcbcf2856849b3549d468ea1 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 18 Sep 2017 11:07:04 -0400 Subject: [PATCH 168/280] lockfile: Use an `initialized` member rather than explicit init This makes us more friendly to being embedded in a GObject or the like that's fully zero-initialized, rather than relying on the special `-1` value for the fd. Calls to `glnx_release_lock_file()` become idempotent, so it's easy to call it unconditionally in an object finalizer. --- glnx-lockfile.c | 12 +++++------- glnx-lockfile.h | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/glnx-lockfile.c b/glnx-lockfile.c index 48f1ea75..cd5c978c 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -126,21 +126,18 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc /* Note that if this is not AT_FDCWD, the caller takes responsibility * for the fd's lifetime being >= that of the lock. */ + out_lock->initialized = TRUE; out_lock->dfd = dfd; - out_lock->path = t; - out_lock->fd = fd; + out_lock->path = g_steal_pointer (&t); + out_lock->fd = glnx_steal_fd (&fd); out_lock->operation = operation; - - fd = -1; - t = NULL; - return TRUE; } void glnx_release_lock_file(GLnxLockFile *f) { int r; - if (!f) + if (!(f && f->initialized)) return; if (f->path) { @@ -181,4 +178,5 @@ void glnx_release_lock_file(GLnxLockFile *f) { (void) close (f->fd); f->fd = -1; f->operation = 0; + f->initialized = FALSE; } diff --git a/glnx-lockfile.h b/glnx-lockfile.h index 6d132e5f..b3465089 100644 --- a/glnx-lockfile.h +++ b/glnx-lockfile.h @@ -27,6 +27,7 @@ #include "glnx-backport-autoptr.h" typedef struct GLnxLockFile { + gboolean initialized; int dfd; char *path; int fd; @@ -37,5 +38,3 @@ gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile void glnx_release_lock_file(GLnxLockFile *f); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxLockFile, glnx_release_lock_file) - -#define GLNX_LOCK_FILE_INIT { .fd = -1, .dfd = AT_FDCWD, .path = NULL } From e30154431d7eea6397e5502b175ba3b50330140f Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 26 Sep 2017 12:55:39 +0100 Subject: [PATCH 169/280] shutil: Fix assertion failure in glnx_shutil_mkdir_p_at() If the directory for @dfd is deleted after being opened, glnx_shutil_mkdir_p_at() would fail with an assertion failure. Fix that, and make it return an ENOENT error instead. Add a unit test. Signed-off-by: Philip Withnall Reviewed-by: Colin Walters Reviewed-by: Jonathan Lebon https://github.com/ostreedev/ostree/issues/1215 --- Makefile-libglnx.am | 6 +++- glnx-shutil.c | 11 ++++++- tests/test-libglnx-shutil.c | 63 +++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/test-libglnx-shutil.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index de6e49be..158063c7 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -53,7 +53,7 @@ libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) -libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros +libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros test-libglnx-shutil TESTS += $(libglnx_tests) check_PROGRAMS += $(libglnx_tests) @@ -72,3 +72,7 @@ test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la test_libglnx_macros_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-macros.c test_libglnx_macros_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_macros_LDADD = $(libglnx_libs) libglnx.la + +test_libglnx_shutil_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-shutil.c +test_libglnx_shutil_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +test_libglnx_shutil_LDADD = $(libglnx_libs) libglnx.la diff --git a/glnx-shutil.c b/glnx-shutil.c index 9124d7a4..8438b5da 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -148,7 +148,13 @@ mkdir_p_at_internal (int dfd, g_assert (!did_recurse); lastslash = strrchr (path, '/'); - g_assert (lastslash != NULL); + if (lastslash == NULL) + { + /* This can happen if @dfd was deleted between being opened and + * passed to mkdir_p_at_internal(). */ + return glnx_throw_errno_prefix (error, "mkdir(%s)", path); + } + /* Note we can mutate the buffer as we dup'd it */ *lastslash = '\0'; @@ -187,6 +193,9 @@ mkdir_p_at_internal (int dfd, * directory fd @dfd. * * See also glnx_ensure_dir() for a non-recursive version. + * + * This will return %G_IO_ERROR_NOT_FOUND if @dfd has been deleted since being + * opened. It may return other errors from mkdirat() in other situations. */ gboolean glnx_shutil_mkdir_p_at (int dfd, diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c new file mode 100644 index 00000000..39f261b8 --- /dev/null +++ b/tests/test-libglnx-shutil.c @@ -0,0 +1,63 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright © 2017 Endless Mobile, Inc. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx.h" +#include +#include +#include +#include +#include + +#include "libglnx-testlib.h" + +static void +test_mkdir_p_enoent (void) +{ + _GLNX_TEST_DECLARE_ERROR(local_error, error); + glnx_fd_close int dfd = -1; + + if (!glnx_ensure_dir (AT_FDCWD, "test", 0755, error)) + return; + if (!glnx_opendirat (AT_FDCWD, "test", FALSE, &dfd, error)) + return; + if (rmdir ("test") < 0) + return (void) glnx_throw_errno_prefix (error, "rmdir(%s)", "test"); + + /* This should fail with ENOENT. */ + glnx_shutil_mkdir_p_at (dfd, "blah/baz", 0755, NULL, error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&local_error); +} + +int +main (int argc, + char **argv) +{ + int ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/mkdir-p/enoent", test_mkdir_p_enoent); + + ret = g_test_run(); + + return ret; +} From dd5fd9c1e558ae5a3916d4c571e1e8c84a9fb098 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 1 Oct 2017 05:20:23 -0700 Subject: [PATCH 170/280] missing: Sync from latest systemd, add memfd_create() Planning to use memfd_create() in flatpak and rpm-ostree, which both use bubblewrap, and want to pass read-only data via file descriptor to the container. Passing via `O_TMPFILE` requires `O_RDWR` (read and write), and passing via a pipe would require buffering. The systemd `missing.h` has grown enormously; I only cherry-picked the bits for memfd. --- glnx-missing-syscall.h | 60 +++++++++++++++++++++++++++++++++++++++++- glnx-missing.h | 54 ++++++++++++++++++++++++++++++++----- libglnx.h | 1 + libglnx.m4 | 4 +-- 4 files changed, 108 insertions(+), 11 deletions(-) diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index c4957e0c..fef6e605 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -18,7 +18,18 @@ along with systemd; If not, see . ***/ -/* Missing glibc definitions to access certain kernel APIs */ +/* Missing glibc definitions to access certain kernel APIs. + This file is last updated from systemd git: + + commit 71e5200f94b22589922704aa4abdf95d4fe2e528 + Author: Daniel Mack + AuthorDate: Tue Oct 18 17:57:10 2016 +0200 + Commit: Lennart Poettering + CommitDate: Fri Sep 22 15:24:54 2017 +0200 + + Add abstraction model for BPF programs +*/ + #if !HAVE_DECL_RENAMEAT2 # ifndef __NR_renameat2 @@ -26,6 +37,8 @@ # define __NR_renameat2 316 # elif defined __arm__ # define __NR_renameat2 382 +# elif defined __aarch64__ +# define __NR_renameat2 276 # elif defined _MIPS_SIM # if _MIPS_SIM == _MIPS_SIM_ABI32 # define __NR_renameat2 4351 @@ -38,6 +51,12 @@ # endif # elif defined __i386__ # define __NR_renameat2 353 +# elif defined __powerpc64__ +# define __NR_renameat2 357 +# elif defined __s390__ || defined __s390x__ +# define __NR_renameat2 347 +# elif defined __arc__ +# define __NR_renameat2 276 # else # warning "__NR_renameat2 unknown for your architecture" # endif @@ -53,6 +72,45 @@ static inline int renameat2(int oldfd, const char *oldname, int newfd, const cha } #endif +#if !HAVE_DECL_MEMFD_CREATE +# ifndef __NR_memfd_create +# if defined __x86_64__ +# define __NR_memfd_create 319 +# elif defined __arm__ +# define __NR_memfd_create 385 +# elif defined __aarch64__ +# define __NR_memfd_create 279 +# elif defined __s390__ +# define __NR_memfd_create 350 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_memfd_create 5314 +# endif +# elif defined __i386__ +# define __NR_memfd_create 356 +# elif defined __arc__ +# define __NR_memfd_create 279 +# else +# warning "__NR_memfd_create unknown for your architecture" +# endif +# endif + +static inline int memfd_create(const char *name, unsigned int flags) { +# ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + /* Copied from systemd git: commit 6bda23dd6aaba50cf8e3e6024248cf736cc443ca Author: Yu Watanabe diff --git a/glnx-missing.h b/glnx-missing.h index a60705a1..0eba07bf 100644 --- a/glnx-missing.h +++ b/glnx-missing.h @@ -19,7 +19,17 @@ along with systemd; If not, see . ***/ -/* Missing glibc definitions to access certain kernel APIs */ +/* Missing glibc definitions to access certain kernel APIs. + This file is last updated from systemd git: + + commit 71e5200f94b22589922704aa4abdf95d4fe2e528 + Author: Daniel Mack + AuthorDate: Tue Oct 18 17:57:10 2016 +0200 + Commit: Lennart Poettering + CommitDate: Fri Sep 22 15:24:54 2017 +0200 + + Add abstraction model for BPF programs +*/ #include #include @@ -29,22 +39,30 @@ #include #include -#if defined(__i386__) || defined(__x86_64__) - -/* The precise definition of __O_TMPFILE is arch specific, so let's - * just define this on x86 where we know the value. */ +/* The precise definition of __O_TMPFILE is arch specific; use the + * values defined by the kernel (note: some are hexa, some are octal, + * duplicated as-is from the kernel definitions): + * - alpha, parisc, sparc: each has a specific value; + * - others: they use the "generic" value. + */ #ifndef __O_TMPFILE +#if defined(__alpha__) +#define __O_TMPFILE 0100000000 +#elif defined(__parisc__) || defined(__hppa__) +#define __O_TMPFILE 0400000000 +#elif defined(__sparc__) || defined(__sparc64__) +#define __O_TMPFILE 0x2000000 +#else #define __O_TMPFILE 020000000 #endif +#endif /* a horrid kludge trying to make sure that this will fail on old kernels */ #ifndef O_TMPFILE #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) #endif -#endif - #ifndef RENAME_NOREPLACE #define RENAME_NOREPLACE (1 << 0) #endif @@ -52,4 +70,26 @@ #define RENAME_EXCHANGE (1 << 1) #endif +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + #include "glnx-missing-syscall.h" diff --git a/libglnx.h b/libglnx.h index 494810d4..411d4fa0 100644 --- a/libglnx.h +++ b/libglnx.h @@ -25,6 +25,7 @@ G_BEGIN_DECLS #include +#include #include #include #include diff --git a/libglnx.m4 b/libglnx.m4 index 9b2e30c9..770f117b 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -1,8 +1,6 @@ AC_DEFUN([LIBGLNX_CONFIGURE], [ -AC_CHECK_DECLS([ - renameat2, - ], +AC_CHECK_DECLS([renameat2, memfd_create], [], [], [[ #include #include From b72906dbe067aeee0edab627c9c8ada0a7d8e808 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 2 Oct 2017 10:07:50 -0400 Subject: [PATCH 171/280] fdio: Generate tmpname for RENAME_EXCHANGE fallback I was using this in rpm-ostree and glanced at the code. This was clearly the intent, but isn't a full fix. See code comments for more details. --- glnx-fdio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/glnx-fdio.c b/glnx-fdio.c index 53f82e27..7dfa5096 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -143,6 +143,13 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, /* Fallback */ { const char *old_tmp_name = glnx_strjoina (oldpath, ".XXXXXX"); + /* This obviously isn't race-free, but doing better gets tricky, since if + * we're here the kernel isn't likely to support RENAME_NOREPLACE either. + * Anyways, upgrade the kernel. Failing that, avoid use of this function in + * shared subdirectories like /tmp. + */ + glnx_gen_temp_name (old_tmp_name); + /* Move old out of the way */ if (renameat (olddirfd, oldpath, olddirfd, old_tmp_name) < 0) return -1; From dea16cd8beff76867bd791b1b9c8b51df8a16057 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 5 Oct 2017 14:45:40 -0400 Subject: [PATCH 172/280] fdio: Squash compiler warning from previous commit Oops. --- glnx-fdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 7dfa5096..892be2a7 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -141,14 +141,14 @@ glnx_renameat2_exchange (int olddirfd, const char *oldpath, #endif /* Fallback */ - { const char *old_tmp_name = glnx_strjoina (oldpath, ".XXXXXX"); - + { char *old_tmp_name_buf = glnx_strjoina (oldpath, ".XXXXXX"); /* This obviously isn't race-free, but doing better gets tricky, since if * we're here the kernel isn't likely to support RENAME_NOREPLACE either. * Anyways, upgrade the kernel. Failing that, avoid use of this function in * shared subdirectories like /tmp. */ - glnx_gen_temp_name (old_tmp_name); + glnx_gen_temp_name (old_tmp_name_buf); + const char *old_tmp_name = old_tmp_name_buf; /* Move old out of the way */ if (renameat (olddirfd, oldpath, olddirfd, old_tmp_name) < 0) From 97cd6a6c1d58a90b52edcff2e2dcb69c8cf7855a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 6 Oct 2017 10:09:18 -0400 Subject: [PATCH 173/280] Add glnx_fd_close() and glnx_autofd I'd like to have the checks for `EBADF` as well as the "assign to -1" in more places. The cleanup function we had for `glnx_fd_close` is actually what we want. Let's rename the cleanup macro to `glnx_autofd` to better match other autocleanups like `g_autofree`. Then we can use `glnx_fd_close()` as a replacement for plain Unix `close()`. I left the `glnx_close_fd` macro, but it's obviously confusing now with the former. We'll eventually remove it. --- glnx-dirfd.c | 3 +-- glnx-local-alloc.h | 38 ++++++++++++++++++++++++++------------ glnx-lockfile.c | 7 ++----- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index ea12c8f9..e6c3319c 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -382,8 +382,7 @@ _glnx_tmpdir_free (GLnxTmpDir *tmpd, if (!(tmpd && tmpd->initialized)) return TRUE; g_assert_cmpint (tmpd->fd, !=, -1); - (void) close (tmpd->fd); - tmpd->fd = -1; + glnx_close_fd (&tmpd->fd); g_assert (tmpd->path); g_assert_cmpint (tmpd->src_dfd, !=, -1); g_autofree char *path = tmpd->path; /* Take ownership */ diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index 46dd9d25..3be1fa43 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -42,14 +42,30 @@ glnx_local_obj_unref (void *v) } #define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) +static inline int +glnx_steal_fd (int *fdp) +{ + int fd = *fdp; + *fdp = -1; + return fd; +} + +/** + * glnx_close_fd: + * @fdp: Pointer to fd + * + * Effectively `close (glnx_steal_fd (&fd))`. Also + * asserts that `close()` did not raise `EBADF` - encountering + * that error is usually a critical bug in the program. + */ static inline void -glnx_cleanup_close_fdp (int *fdp) +glnx_close_fd (int *fdp) { - int fd, errsv; + int errsv; g_assert (fdp); - fd = *fdp; + int fd = glnx_steal_fd (fdp); if (fd >= 0) { errsv = errno; @@ -62,16 +78,14 @@ glnx_cleanup_close_fdp (int *fdp) /** * glnx_fd_close: * + * Deprecated in favor of `glnx_autofd`. + */ +#define glnx_fd_close __attribute__((cleanup(glnx_close_fd))) +/** + * glnx_autofd: + * * Call close() on a variable location when it goes out of scope. */ -#define glnx_fd_close __attribute__((cleanup(glnx_cleanup_close_fdp))) - -static inline int -glnx_steal_fd (int *fdp) -{ - int fd = *fdp; - *fdp = -1; - return fd; -} +#define glnx_autofd __attribute__((cleanup(glnx_close_fd))) G_END_DECLS diff --git a/glnx-lockfile.c b/glnx-lockfile.c index cd5c978c..2956edaf 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -119,8 +119,7 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc if (st.st_nlink > 0) break; - (void) close(fd); - fd = -1; + glnx_close_fd (&fd); } /* Note that if this is not AT_FDCWD, the caller takes responsibility @@ -174,9 +173,7 @@ void glnx_release_lock_file(GLnxLockFile *f) { f->path = NULL; } - if (f->fd != -1) - (void) close (f->fd); - f->fd = -1; + glnx_close_fd (&f->fd); f->operation = 0; f->initialized = FALSE; } From 5362f6bc3ff3e30f379e767b203d15c9e56d6f08 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 6 Oct 2017 21:26:41 +0000 Subject: [PATCH 174/280] fdio: allow NULL for fstatat_allow_noent stbuf Often, the caller doesn't actually care about the details of the stat struct itself, but just whether the entry exists or not. It does work to just pass `NULL` directly to glibc in a quick test, but given that the argument is tagged as `__nonnull` and that the documentation does not explicitly specify this is supported, let's do this safely. --- glnx-fdio.h | 5 +++-- tests/test-libglnx-fdio.c | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 518135c5..1aa0c434 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -299,7 +299,7 @@ glnx_fstatat (int dfd, * glnx_fstatat_allow_noent: * @dfd: Directory FD to stat beneath * @path: Path to stat beneath @dfd - * @buf: (out caller-allocates): Return location for stat details + * @buf: (out caller-allocates) (allow-none): Return location for stat details * @flags: Flags to pass to fstatat() * @error: Return location for a #GError, or %NULL * @@ -318,7 +318,8 @@ glnx_fstatat_allow_noent (int dfd, int flags, GError **error) { - if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf, flags)) != 0) + struct stat stbuf; + if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf ?: &stbuf, flags)) != 0) { if (errno != ENOENT) { diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index bf973b9a..350294c4 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -161,6 +161,16 @@ test_fstatat (void) return; g_assert_cmpint (errno, ==, ENOENT); g_assert_no_error (local_error); + + /* test NULL parameter for stat */ + if (!glnx_fstatat_allow_noent (AT_FDCWD, ".", NULL, 0, error)) + return; + g_assert_cmpint (errno, ==, 0); + g_assert_no_error (local_error); + if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchfile", NULL, 0, error)) + return; + g_assert_cmpint (errno, ==, ENOENT); + g_assert_no_error (local_error); } static void From b923a950af1b3cfc4ba1fc877428928ffd4f38ec Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 11 Oct 2017 20:02:56 +0000 Subject: [PATCH 175/280] tests: drop unused variable --- tests/test-libglnx-fdio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 350294c4..c5ff9492 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -177,7 +177,6 @@ static void test_filecopy (void) { _GLNX_TEST_DECLARE_ERROR(local_error, error); - g_auto(GLnxTmpfile) tmpf = { 0, }; const char foo[] = "foo"; struct stat stbuf; From e627524af9ae499c1edd04795442f8bdb05b5223 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 11 Oct 2017 17:06:40 -0400 Subject: [PATCH 176/280] fdio: Avoid ?: syntax for fstatat_allow_noent() `g-ir-scanner` is unaware of this GNUC extension and complains. Saw that while building ostree. While we're here, fix up a few other things: - Tell the compiler the stat buffer is unused (I didn't see a warning, just doing this on general principle) - Return from `glnx_throw_errno_prefix()` directly; we do preserve errno there, let's feel free to rely on it. --- glnx-fdio.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 1aa0c434..dc93b687 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -318,16 +318,12 @@ glnx_fstatat_allow_noent (int dfd, int flags, GError **error) { - struct stat stbuf; - if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf ?: &stbuf, flags)) != 0) + G_GNUC_UNUSED struct stat unused_stbuf; + if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf ? out_buf : &unused_stbuf, flags)) != 0) { if (errno != ENOENT) - { - int errsv = errno; - (void) glnx_throw_errno_prefix (error, "fstatat(%s)", path); - errno = errsv; - return FALSE; - } + return glnx_throw_errno_prefix (error, "fstatat(%s)", path); + /* Note we preserve errno as ENOENT */ } else errno = 0; From 771d7a01d1104bbb8240cb29393314c7a4a69c4d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 16 Oct 2017 14:09:39 -0400 Subject: [PATCH 177/280] tree-wide: Use glnx_autofd and glnx_close_fd() Port to `glnx_autofd` tree wide, and add one missed `glnx_close_fd()` use in the tmpfile code. --- README.md | 2 +- glnx-dirfd.c | 4 ++-- glnx-fdio.c | 12 ++++-------- glnx-lockfile.c | 2 +- glnx-shutil.c | 2 +- tests/test-libglnx-fdio.c | 12 ++++++------ tests/test-libglnx-shutil.c | 2 +- tests/test-libglnx-xattrs.c | 6 +++--- 8 files changed, 19 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8fb2faa4..f2ae6ae3 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ applicable. For local allocation macros, you should start using the `g_auto` macros from GLib. A backport is included in libglnx. There are a few -APIs not defined in GLib yet, such as `glnx_fd_close`. +APIs not defined in GLib yet, such as `glnx_autofd`. `gs_transfer_out_value` is replaced by `g_steal_pointer`. diff --git a/glnx-dirfd.c b/glnx-dirfd.c index e6c3319c..e0c244cf 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -99,7 +99,7 @@ glnx_dirfd_iterator_init_at (int dfd, GLnxDirFdIterator *out_dfd_iter, GError **error) { - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; if (!glnx_opendirat (dfd, path, follow, &fd, error)) return FALSE; @@ -326,7 +326,7 @@ glnx_mkdtempat (int dfd, const char *tmpl, int mode, } /* And open it */ - glnx_fd_close int ret_dfd = -1; + glnx_autofd int ret_dfd = -1; if (!glnx_opendirat (dfd, path, FALSE, &ret_dfd, error)) { /* If we fail to open, let's try to clean up */ diff --git a/glnx-fdio.c b/glnx-fdio.c index 892be2a7..b24d03f2 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -175,11 +175,7 @@ glnx_tmpfile_clear (GLnxTmpfile *tmpf) return; if (!tmpf->initialized) return; - if (tmpf->fd != -1) - { - if (close (tmpf->fd) < 0) - g_assert (errno != EBADF); - } + glnx_close_fd (&tmpf->fd); /* If ->path is set, we're likely aborting due to an error. Clean it up */ if (tmpf->path) { @@ -196,7 +192,7 @@ open_tmpfile_core (int dfd, const char *subpath, GError **error) { const guint mode = 0600; - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; int count; dfd = glnx_dirfd_canonicalize (dfd); @@ -576,7 +572,7 @@ glnx_file_get_contents_utf8_at (int dfd, { dfd = glnx_dirfd_canonicalize (dfd); - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; if (!glnx_openat_rdonly (dfd, subpath, TRUE, &fd, error)) return NULL; @@ -933,7 +929,7 @@ glnx_file_copy_at (int src_dfd, /* Regular file path below here */ - glnx_fd_close int src_fd = -1; + glnx_autofd int src_fd = -1; if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) return FALSE; diff --git a/glnx-lockfile.c b/glnx-lockfile.c index 2956edaf..f1d52dee 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -61,7 +61,7 @@ */ gboolean glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) { - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; g_autofree char *t = NULL; int r; diff --git a/glnx-shutil.c b/glnx-shutil.c index 8438b5da..8014c0cd 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -84,7 +84,7 @@ glnx_shutil_rm_rf_at (int dfd, GCancellable *cancellable, GError **error) { - glnx_fd_close int target_dfd = -1; + glnx_autofd int target_dfd = -1; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; dfd = glnx_dirfd_canonicalize (dfd); diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index c5ff9492..81636e59 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -32,8 +32,8 @@ static gboolean renameat_test_setup (int *out_srcfd, int *out_destfd, GError **error) { - glnx_fd_close int srcfd = -1; - glnx_fd_close int destfd = -1; + glnx_autofd int srcfd = -1; + glnx_autofd int destfd = -1; (void) glnx_shutil_rm_rf_at (AT_FDCWD, "srcdir", NULL, NULL); if (mkdir ("srcdir", 0755) < 0) @@ -62,8 +62,8 @@ static void test_renameat2_noreplace (void) { _GLNX_TEST_DECLARE_ERROR(local_error, error); - glnx_fd_close int srcfd = -1; - glnx_fd_close int destfd = -1; + glnx_autofd int srcfd = -1; + glnx_autofd int destfd = -1; struct stat stbuf; if (!renameat_test_setup (&srcfd, &destfd, error)) @@ -92,8 +92,8 @@ test_renameat2_exchange (void) { _GLNX_TEST_DECLARE_ERROR(local_error, error); - glnx_fd_close int srcfd = -1; - glnx_fd_close int destfd = -1; + glnx_autofd int srcfd = -1; + glnx_autofd int destfd = -1; if (!renameat_test_setup (&srcfd, &destfd, error)) return; diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index 39f261b8..6917b890 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -32,7 +32,7 @@ static void test_mkdir_p_enoent (void) { _GLNX_TEST_DECLARE_ERROR(local_error, error); - glnx_fd_close int dfd = -1; + glnx_autofd int dfd = -1; if (!glnx_ensure_dir (AT_FDCWD, "test", 0755, error)) return; diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 0076b712..63e12314 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -82,7 +82,7 @@ do_write_run (GLnxDirFdIterator *dfd_iter, GError **error) { guint32 randname_v = g_random_int (); g_autofree char *randname = g_strdup_printf ("file%u", randname_v); - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; again: fd = openat (dfd_iter->fd, randname, O_CREAT | O_EXCL, 0644); @@ -113,7 +113,7 @@ do_write_run (GLnxDirFdIterator *dfd_iter, GError **error) if (!dent) break; - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error)) return FALSE; @@ -157,7 +157,7 @@ do_read_run (GLnxDirFdIterator *dfd_iter, if (!dent) break; - glnx_fd_close int fd = -1; + glnx_autofd int fd = -1; if (!glnx_openat_rdonly (dfd_iter->fd, dent->d_name, FALSE, &fd, error)) return FALSE; From d15a3790074fd982f2611a5b450dea61052dfc0b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 16 Oct 2017 14:16:16 -0400 Subject: [PATCH 178/280] tree-wide: Some new style porting Use decl-after-stmt where applicable. --- glnx-dirfd.c | 21 ++++++---------- glnx-fdio.c | 70 +++++++++++++++++++++++---------------------------- glnx-shutil.c | 9 +++---- 3 files changed, 43 insertions(+), 57 deletions(-) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index e0c244cf..6d1e2d21 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -190,15 +190,12 @@ glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, GError **error) { - struct dirent *ret_dent; - g_return_val_if_fail (out_dent, FALSE); if (!glnx_dirfd_iterator_next_dent (dfd_iter, out_dent, cancellable, error)) return FALSE; - ret_dent = *out_dent; - + struct dirent *ret_dent = *out_dent; if (ret_dent) { @@ -265,20 +262,16 @@ glnx_fdrel_abspath (int dfd, void glnx_gen_temp_name (gchar *tmpl) { - size_t len; - char *XXXXXX; - int i; - static const char letters[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - static const int NLETTERS = sizeof (letters) - 1; - g_return_if_fail (tmpl != NULL); - len = strlen (tmpl); + const size_t len = strlen (tmpl); g_return_if_fail (len >= 6); - XXXXXX = tmpl + (len - 6); + static const char letters[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + static const int NLETTERS = sizeof (letters) - 1; - for (i = 0; i < 6; i++) + char *XXXXXX = tmpl + (len - 6); + for (int i = 0; i < 6; i++) XXXXXX[i] = letters[g_random_int_range(0, NLETTERS)]; } diff --git a/glnx-fdio.c b/glnx-fdio.c index b24d03f2..a1f19037 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -191,9 +191,8 @@ open_tmpfile_core (int dfd, const char *subpath, GLnxTmpfile *out_tmpf, GError **error) { + /* Picked this to match mkstemp() */ const guint mode = 0600; - glnx_autofd int fd = -1; - int count; dfd = glnx_dirfd_canonicalize (dfd); @@ -204,33 +203,35 @@ open_tmpfile_core (int dfd, const char *subpath, * link_tmpfile() below to rename the result after writing the file * in full. */ #if defined(O_TMPFILE) && !defined(DISABLE_OTMPFILE) && !defined(ENABLE_WRPSEUDO_COMPAT) - fd = openat (dfd, subpath, O_TMPFILE|flags, mode); - if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP))) - return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); - if (fd != -1) - { - /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523 - * See also https://github.com/ostreedev/ostree/issues/991 - */ - if (fchmod (fd, mode) < 0) - return glnx_throw_errno_prefix (error, "fchmod"); - out_tmpf->initialized = TRUE; - out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ - out_tmpf->fd = glnx_steal_fd (&fd); - out_tmpf->path = NULL; - return TRUE; - } + { + glnx_autofd int fd = openat (dfd, subpath, O_TMPFILE|flags, mode); + if (fd == -1 && !(G_IN_SET(errno, ENOSYS, EISDIR, EOPNOTSUPP))) + return glnx_throw_errno_prefix (error, "open(O_TMPFILE)"); + if (fd != -1) + { + /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523 + * See also https://github.com/ostreedev/ostree/issues/991 + */ + if (fchmod (fd, mode) < 0) + return glnx_throw_errno_prefix (error, "fchmod"); + out_tmpf->initialized = TRUE; + out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ + out_tmpf->fd = glnx_steal_fd (&fd); + out_tmpf->path = NULL; + return TRUE; + } + } /* Fallthrough */ #endif + const guint count_max = 100; { g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL); - const guint count_max = 100; - for (count = 0; count < count_max; count++) + for (int count = 0; count < count_max; count++) { glnx_gen_temp_name (tmp); - fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, mode); + glnx_autofd int fd = openat (dfd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, mode); if (fd < 0) { if (errno == EEXIST) @@ -249,7 +250,7 @@ open_tmpfile_core (int dfd, const char *subpath, } } g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, - "Exhausted %u attempts to create temporary file", count); + "Exhausted %u attempts to create temporary file", count_max); return FALSE; } @@ -359,9 +360,9 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, char *dnbuf = strdupa (target); const char *dn = dirname (dnbuf); char *tmpname_buf = glnx_strjoina (dn, "/tmp.XXXXXX"); - guint count; - const guint count_max = 100; + const guint count_max = 100; + guint count; for (count = 0; count < count_max; count++) { glnx_gen_temp_name (tmpname_buf); @@ -602,17 +603,13 @@ glnx_readlinkat_malloc (int dfd, GCancellable *cancellable, GError **error) { - size_t l = 100; - dfd = glnx_dirfd_canonicalize (dfd); + size_t l = 100; for (;;) { - g_autofree char *c = NULL; - ssize_t n; - - c = g_malloc (l); - n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); + g_autofree char *c = g_malloc (l); + ssize_t n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1)); if (n < 0) return glnx_null_throw_errno_prefix (error, "readlinkat"); @@ -682,18 +679,15 @@ copy_symlink_at (int src_dfd, int glnx_loop_write(int fd, const void *buf, size_t nbytes) { - const uint8_t *p = buf; - - g_return_val_if_fail(fd >= 0, -1); - g_return_val_if_fail(buf, -1); + g_return_val_if_fail (fd >= 0, -1); + g_return_val_if_fail (buf, -1); errno = 0; + const uint8_t *p = buf; while (nbytes > 0) { - ssize_t k; - - k = write(fd, p, nbytes); + ssize_t k = write(fd, p, nbytes); if (k < 0) { if (errno == EINTR) diff --git a/glnx-shutil.c b/glnx-shutil.c index 8014c0cd..75d05931 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -84,14 +84,12 @@ glnx_shutil_rm_rf_at (int dfd, GCancellable *cancellable, GError **error) { - glnx_autofd int target_dfd = -1; - g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; - dfd = glnx_dirfd_canonicalize (dfd); + /* With O_NOFOLLOW first */ - target_dfd = openat (dfd, path, - O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); + glnx_autofd int target_dfd = + openat (dfd, path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); if (target_dfd == -1) { @@ -110,6 +108,7 @@ glnx_shutil_rm_rf_at (int dfd, } else { + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_take_fd (&target_dfd, &dfd_iter, error)) return FALSE; From 4577dc826a923d976ad1a98b4ed5ecc36eac6efe Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 2 Nov 2017 14:55:17 +0000 Subject: [PATCH 179/280] Add missing configure check for copy_file_range Without this, including glnx-missing-syscall.h raises this warning: ../ext/libglnx/glnx-missing-syscall.h:121:6: warning: "HAVE_DECL_COPY_FILE_RANGE" is not defined [-Wundef] #if !HAVE_DECL_COPY_FILE_RANGE ^~~~~~~~~~~~~~~~~~~~~~~~~ --- libglnx.m4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libglnx.m4 b/libglnx.m4 index 770f117b..d5bcc2f1 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -1,6 +1,9 @@ AC_DEFUN([LIBGLNX_CONFIGURE], [ -AC_CHECK_DECLS([renameat2, memfd_create], +AC_CHECK_DECLS([ + renameat2, + memfd_create, + copy_file_range], [], [], [[ #include #include From b36606b366d39c7ddb90ee21d622c0cb1da118ed Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 2 Nov 2017 11:30:53 -0400 Subject: [PATCH 180/280] missing-syscall: #include config.h Followup to previous commit; this helps me build libostree with `-Werror=undef`. --- glnx-missing-syscall.h | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index fef6e605..ebfd7f47 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -30,6 +30,7 @@ Add abstraction model for BPF programs */ +#include "config.h" #if !HAVE_DECL_RENAMEAT2 # ifndef __NR_renameat2 From 016ea9168b1c372f9a09063245f9aab74434616f Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 2 Nov 2017 16:08:27 +0000 Subject: [PATCH 181/280] errors: don't use 'static inline' on varargs functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This caused GCC 6.3.0 -Winline to complain: ../../../ext/libglnx/glnx-errors.h:169:1: warning: function ‘glnx_throw_errno_prefix’ can never be inlined because it uses variable argument lists [-Winline] glnx_throw_errno_prefix (GError **error, const char *fmt, ...) ^~~~~~~~~~~~~~~~~~~~~~~ ../../../ext/libglnx/glnx-errors.h:169:1: warning: inlining failed in call to ‘glnx_throw_errno_prefix’: function not inlinable [-Winline] --- glnx-errors.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ glnx-errors.h | 69 +++-------------------------------------------- 2 files changed, 78 insertions(+), 66 deletions(-) diff --git a/glnx-errors.c b/glnx-errors.c index 48008733..f350f305 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -23,6 +23,32 @@ #include #include +/* Set @error with G_IO_ERROR/G_IO_ERROR_FAILED. + * + * This function returns %FALSE so it can be used conveniently in a single + * statement: + * + * ``` + * if (strcmp (foo, "somevalue") != 0) + * return glnx_throw (error, "key must be somevalue, not '%s'", foo); + * ``` + */ +gboolean +glnx_throw (GError **error, + const char *fmt, + ...) +{ + if (error == NULL) + return FALSE; + + va_list args; + va_start (args, fmt); + GError *new = g_error_new_valist (G_IO_ERROR, G_IO_ERROR_FAILED, fmt, args); + va_end (args); + g_propagate_error (error, g_steal_pointer (&new)); + return FALSE; +} + void glnx_real_set_prefix_error_va (GError *error, const char *format, @@ -39,6 +65,30 @@ glnx_real_set_prefix_error_va (GError *error, error->message = g_string_free (g_steal_pointer (&buf), FALSE); } +/* Prepend to @error's message by `$prefix: ` where `$prefix` is computed via + * printf @fmt. Returns %FALSE so it can be used conveniently in a single + * statement: + * + * ``` + * if (!function_that_fails (s, error)) + * return glnx_throw_prefix (error, "while handling '%s'", s); + * ``` + * */ +gboolean +glnx_prefix_error (GError **error, + const char *fmt, + ...) +{ + if (error == NULL) + return FALSE; + + va_list args; + va_start (args, fmt); + glnx_real_set_prefix_error_va (*error, fmt, args); + va_end (args); + return FALSE; +} + void glnx_real_set_prefix_error_from_errno_va (GError **error, gint errsv, @@ -54,3 +104,28 @@ glnx_real_set_prefix_error_from_errno_va (GError **error, g_strerror (errsv)); glnx_real_set_prefix_error_va (*error, format, args); } + +/* Set @error using the value of `$prefix: g_strerror (errno)` where `$prefix` + * is computed via printf @fmt. + * + * This function returns %FALSE so it can be used conveniently in a single + * statement: + * + * ``` + * return glnx_throw_errno_prefix (error, "unlinking %s", pathname); + * ``` + */ +gboolean +glnx_throw_errno_prefix (GError **error, + const char *fmt, + ...) +{ + int errsv = errno; + va_list args; + va_start (args, fmt); + glnx_real_set_prefix_error_from_errno_va (error, errsv, fmt, args); + va_end (args); + /* See comment in glnx_throw_errno() about preserving errno */ + errno = errsv; + return FALSE; +} diff --git a/glnx-errors.h b/glnx-errors.h index 5a6fe196..cbe74a60 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -25,29 +25,7 @@ G_BEGIN_DECLS -/* Set @error with G_IO_ERROR/G_IO_ERROR_FAILED. - * - * This function returns %FALSE so it can be used conveniently in a single - * statement: - * - * ``` - * if (strcmp (foo, "somevalue") != 0) - * return glnx_throw (error, "key must be somevalue, not '%s'", foo); - * ``` - */ -static inline gboolean G_GNUC_PRINTF (2,3) -glnx_throw (GError **error, const char *fmt, ...) -{ - if (error == NULL) - return FALSE; - - va_list args; - va_start (args, fmt); - GError *new = g_error_new_valist (G_IO_ERROR, G_IO_ERROR_FAILED, fmt, args); - va_end (args); - g_propagate_error (error, g_steal_pointer (&new)); - return FALSE; -} +gboolean glnx_throw (GError **error, const char *fmt, ...) G_GNUC_PRINTF (2,3); /* Like `glnx_throw ()`, but returns %NULL. */ #define glnx_null_throw(error, args...) \ @@ -58,27 +36,7 @@ void glnx_real_set_prefix_error_va (GError *error, const char *format, va_list args) G_GNUC_PRINTF (2,0); -/* Prepend to @error's message by `$prefix: ` where `$prefix` is computed via - * printf @fmt. Returns %FALSE so it can be used conveniently in a single - * statement: - * - * ``` - * if (!function_that_fails (s, error)) - * return glnx_throw_prefix (error, "while handling '%s'", s); - * ``` - * */ -static inline gboolean G_GNUC_PRINTF (2,3) -glnx_prefix_error (GError **error, const char *fmt, ...) -{ - if (error == NULL) - return FALSE; - - va_list args; - va_start (args, fmt); - glnx_real_set_prefix_error_va (*error, fmt, args); - va_end (args); - return FALSE; -} +gboolean glnx_prefix_error (GError **error, const char *fmt, ...) G_GNUC_PRINTF (2,3); /* Like `glnx_prefix_error ()`, but returns %NULL. */ #define glnx_prefix_error_null(error, args...) \ @@ -155,28 +113,7 @@ void glnx_real_set_prefix_error_from_errno_va (GError **error, const char *format, va_list args) G_GNUC_PRINTF (3,0); -/* Set @error using the value of `$prefix: g_strerror (errno)` where `$prefix` - * is computed via printf @fmt. - * - * This function returns %FALSE so it can be used conveniently in a single - * statement: - * - * ``` - * return glnx_throw_errno_prefix (error, "unlinking %s", pathname); - * ``` - */ -static inline gboolean G_GNUC_PRINTF (2,3) -glnx_throw_errno_prefix (GError **error, const char *fmt, ...) -{ - int errsv = errno; - va_list args; - va_start (args, fmt); - glnx_real_set_prefix_error_from_errno_va (error, errsv, fmt, args); - va_end (args); - /* See comment above about preserving errno */ - errno = errsv; - return FALSE; -} +gboolean glnx_throw_errno_prefix (GError **error, const char *fmt, ...) G_GNUC_PRINTF (2,3); /* Like glnx_throw_errno_prefix(), but yields a NULL pointer. */ #define glnx_null_throw_errno_prefix(error, args...) \ From 03b48a39347041112a36d379d8d3c969372255c6 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 10 Nov 2017 09:23:15 -0500 Subject: [PATCH 182/280] fdio: implement glnx_basename from scratch At the top of glnx-fdio.h there's this comment: /* When we include libgen.h because we need * dirname() we immediately undefine * basename() since libgen.h defines it as * a macro to the XDG version which is really * broken. */ and then it does #undef basename to try to gain access to non-default basename implementation. The problem is that this trick doesn't work on some systems: ./libglnx/glnx-fdio.h: In function 'glnx_basename': ./libglnx/glnx-fdio.h:46:11: error: 'basename' undeclared (first use in this function) return (basename) (path); Anyway, basename() is like 3 lines of code to implement, so this commit just does that instead of relying on glibc for it. --- glnx-fdio.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index dc93b687..8af54ff2 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -29,12 +29,6 @@ #include #include #include -/* From systemd/src/shared/util.h */ -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the XDG - * version which is really broken. */ -#include -#undef basename #include #include @@ -47,7 +41,14 @@ G_BEGIN_DECLS static inline const char *glnx_basename (const char *path) { - return (basename) (path); + gchar *base; + + base = strrchr (path, G_DIR_SEPARATOR); + + if (base) + return base + 1; + + return path; } /* Utilities for standard FILE* */ From 31ef1961ec6ee57db910911b3af13698535ac7b3 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Dec 2017 14:14:53 -0500 Subject: [PATCH 183/280] fdio: Include libgen.h again for dirname() rpm-ostree at least uses `dirname()` and relied on the `#include ` that we had previously. --- glnx-fdio.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index 8af54ff2..c0a7cc1d 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -29,6 +29,8 @@ #include #include #include +// For dirname(), and previously basename() +#include #include #include @@ -41,9 +43,7 @@ G_BEGIN_DECLS static inline const char *glnx_basename (const char *path) { - gchar *base; - - base = strrchr (path, G_DIR_SEPARATOR); + gchar *base = strrchr (path, G_DIR_SEPARATOR); if (base) return base + 1; From a8f96bd5f7e5a0cec6e4c40774e0eea99d0bd0c8 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Dec 2017 14:06:08 -0500 Subject: [PATCH 184/280] console: Add an "n items" API It's often really useful to see the counts, not just the percentage. --- glnx-console.c | 26 ++++++++++++++++++++++++++ glnx-console.h | 6 +++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index c6d93311..38747652 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -280,6 +280,32 @@ glnx_console_progress_text_percent (const char *text, text_percent_internal (text, percentage); } +/** + * glnx_console_progress_n_items: + * @text: Show this text before the progress bar + * @current: An integer for how many items have been processed + * @total: An integer for how many items there are total + * + * On a tty, print to the console @text followed by [@current/@total], + * then an ASCII art progress bar, like glnx_console_progress_text_percent(). + * + * You must have called glnx_console_lock() before invoking this + * function. + */ +void +glnx_console_progress_n_items (const char *text, + guint current, + guint total) +{ + g_return_if_fail (current <= total); + g_return_if_fail (total > 0); + + g_autofree char *newtext = g_strdup_printf ("%s (%u/%u)", text, current, total); + /* Special case current == total to ensure we end at 100% */ + int percentage = (current == total) ? 100 : (((double)current) / total * 100); + glnx_console_progress_text_percent (newtext, percentage); +} + void glnx_console_text (const char *text) { diff --git a/glnx-console.h b/glnx-console.h index 8c1d8115..108dc407 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -36,7 +36,11 @@ void glnx_console_lock (GLnxConsoleRef *ref); void glnx_console_text (const char *text); void glnx_console_progress_text_percent (const char *text, - guint percentage); + guint percentage); + +void glnx_console_progress_n_items (const char *text, + guint current, + guint total); void glnx_console_unlock (GLnxConsoleRef *ref); From bb7dd4dd085d0e1805a39478cac437d35313d22b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Dec 2017 14:10:47 -0500 Subject: [PATCH 185/280] console: Limit progress bar to 20 columns max IMO, it looks bad to have a really big progress bar. I personally usually work on a 27" monitor with maximized terminals. --- glnx-console.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index 38747652..8654c7bf 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -29,6 +29,13 @@ #include #include +/* For people with widescreen monitors and maximized terminals, it looks pretty + * bad to have an enormous progress bar. For much the same reason as web pages + * tend to have a maximum width; + * https://ux.stackexchange.com/questions/48982/suggest-good-max-width-for-fluid-width-design + */ +#define MAX_PROGRESSBAR_COLUMNS 20 + static char *current_text = NULL; static gint current_percent = -1; static gboolean locked; @@ -232,7 +239,7 @@ text_percent_internal (const char *text, else { const guint textlen = MIN (input_textlen, ncolumns - bar_min); - const guint barlen = ncolumns - (textlen + 1);; + const guint barlen = MIN (MAX_PROGRESSBAR_COLUMNS, ncolumns - (textlen + 1)); if (textlen > 0) { @@ -245,7 +252,7 @@ text_percent_internal (const char *text, const guint textpercent_len = 5; const guint bar_internal_len = barlen - nbraces - textpercent_len; const guint eqlen = bar_internal_len * (percentage / 100.0); - const guint spacelen = bar_internal_len - eqlen; + const guint spacelen = bar_internal_len - eqlen; fputc ('[', stdout); printpad (equals, n_equals, eqlen); From 985ed1c861abf4a0069b582ed6a26f6728e673c2 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Dec 2017 15:08:16 -0500 Subject: [PATCH 186/280] console: Drop unused current_text/current_percent The idea was clearly to avoid useless updates, but we never actually *set* these values. Drop the code for now to avoid confusion, I'll reimplement this in a better way. --- glnx-console.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index 8654c7bf..2ca13494 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -36,8 +36,6 @@ */ #define MAX_PROGRESSBAR_COLUMNS 20 -static char *current_text = NULL; -static gint current_percent = -1; static gboolean locked; static gboolean @@ -154,8 +152,6 @@ glnx_console_lock (GLnxConsoleRef *console) locked = console->locked = TRUE; - current_percent = 0; - if (console->is_tty) { if (g_once_init_enter (&sigwinch_initialized)) @@ -200,10 +196,6 @@ text_percent_internal (const char *text, const guint input_textlen = text ? strlen (text) : 0; - if (percentage == current_percent - && g_strcmp0 (text, current_text) == 0) - return; - if (!stdout_is_tty ()) { if (text) @@ -332,9 +324,6 @@ glnx_console_unlock (GLnxConsoleRef *console) g_return_if_fail (locked); g_return_if_fail (console->locked); - current_percent = -1; - g_clear_pointer (¤t_text, g_free); - if (console->is_tty) fputc ('\n', stdout); From 96b1fd9578b3d6ff2d8e0707068f5ef450eba98c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 12 Dec 2017 15:20:08 -0500 Subject: [PATCH 187/280] console: Avoid rendering too quickly For TTYs, it slows things down pointlessly. For non-TTYs like Jenkins jobs, it creates a ton of output that obscures useful information. --- glnx-console.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/glnx-console.c b/glnx-console.c index 2ca13494..1cb3a497 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -36,7 +36,15 @@ */ #define MAX_PROGRESSBAR_COLUMNS 20 +/* Max updates output per second. On a tty there's no point to rendering + * extremely fast; and for a non-tty we're probably in a Jenkins job + * or whatever and having percentages spam multiple lines there is annoying. + */ +#define MAX_TTY_UPDATE_HZ (5) +#define MAX_NONTTY_UPDATE_HZ (1) + static gboolean locked; +static guint64 last_update_ms; /* monotonic time in millis we last updated */ static gboolean stdout_is_tty (void) @@ -184,6 +192,26 @@ static void text_percent_internal (const char *text, int percentage) { + /* Check whether we're trying to render too fast; unless percentage is 100, in + * which case we assume this is the last call, so we always render it. + */ + const guint64 current_ms = g_get_monotonic_time () / 1000; + if (percentage != 100) + { + const guint64 diff_ms = current_ms - last_update_ms; + if (stdout_is_tty ()) + { + if (diff_ms < (1000/MAX_TTY_UPDATE_HZ)) + return; + } + else + { + if (diff_ms < (1000/MAX_NONTTY_UPDATE_HZ)) + return; + } + } + last_update_ms = current_ms; + static const char equals[] = "===================="; const guint n_equals = sizeof (equals) - 1; static const char spaces[] = " "; From 6f1ee5db1400b13a9a0fa0b2274ae34e8710c1aa Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Wed, 10 Jan 2018 17:51:56 +0000 Subject: [PATCH 188/280] console: make stdout_is_tty() public Share its static var goodness with clients. This will be used in rpm-ostree from various places which sometimes do use a `GLnxConsole` and sometimes don't, so it's more convenient to make it its own function. --- glnx-console.c | 10 +++++----- glnx-console.h | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/glnx-console.c b/glnx-console.c index 1cb3a497..88130745 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -46,8 +46,8 @@ static gboolean locked; static guint64 last_update_ms; /* monotonic time in millis we last updated */ -static gboolean -stdout_is_tty (void) +gboolean +glnx_stdout_is_tty (void) { static gsize initialized = 0; static gboolean stdout_is_tty_v; @@ -156,7 +156,7 @@ glnx_console_lock (GLnxConsoleRef *console) g_return_if_fail (!locked); g_return_if_fail (!console->locked); - console->is_tty = stdout_is_tty (); + console->is_tty = glnx_stdout_is_tty (); locked = console->locked = TRUE; @@ -199,7 +199,7 @@ text_percent_internal (const char *text, if (percentage != 100) { const guint64 diff_ms = current_ms - last_update_ms; - if (stdout_is_tty ()) + if (glnx_stdout_is_tty ()) { if (diff_ms < (1000/MAX_TTY_UPDATE_HZ)) return; @@ -224,7 +224,7 @@ text_percent_internal (const char *text, const guint input_textlen = text ? strlen (text) : 0; - if (!stdout_is_tty ()) + if (!glnx_stdout_is_tty ()) { if (text) fprintf (stdout, "%s", text); diff --git a/glnx-console.h b/glnx-console.h index 108dc407..d853a80c 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -31,6 +31,8 @@ struct GLnxConsoleRef { typedef struct GLnxConsoleRef GLnxConsoleRef; +gboolean glnx_stdout_is_tty (void); + void glnx_console_lock (GLnxConsoleRef *ref); void glnx_console_text (const char *text); From 0c82203cd459a35cc3f471e3205355e9fb79160f Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Sat, 17 Feb 2018 23:57:53 +0100 Subject: [PATCH 189/280] Include sys/mman.h when checking for memfd_create glibc 2.27 added support for memfd_create. Unfortunately flatpak-builder, or rather the included libglnx library, also has such a function to wrap the corresponding syscall. It correctly tries to detect it in the configure script to disabled the wrapper in case glibc provides it. However it doesn't work due to a missing include. Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=890722 --- libglnx.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/libglnx.m4 b/libglnx.m4 index d5bcc2f1..34caf204 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -12,6 +12,7 @@ AC_CHECK_DECLS([ #include #include #include +#include ]]) AC_ARG_ENABLE(otmpfile, From 03e16afa7f2cf7ce517b604035683fa0409f730c Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 4 May 2018 13:35:34 -0400 Subject: [PATCH 190/280] missing-syscalls: Use different name for copy_file_range In glibc 2.27, a wrapper for `copy_file_range` was added[1]. The function is now always defined, either using a userspace fallback or just returning `ENOSYS` if the kernel doesn't support it. This throws off our preprocessor conditionals. Work around this by just renaming our implementation differently. This is similar to what systemd did[2]. Hit this when trying to build on F28, which rebased to glibc 2.27. [1] https://sourceware.org/git/?p=glibc.git;a=commit;h=bad7a0c81f501fbbcc79af9eaa4b8254441c4a1f [2] https://github.com/systemd/systemd/commit/5187dd2c403caf92d09f3491e41f1ceb3f10491f --- glnx-missing-syscall.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index ebfd7f47..4876ca39 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -140,10 +140,10 @@ static inline int memfd_create(const char *name, unsigned int flags) { # endif # endif -static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { +static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { # ifdef __NR_copy_file_range return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); # else @@ -151,4 +151,6 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, return -1; # endif } + +# define copy_file_range missing_copy_file_range #endif From 97b5c08d2f93dc93ba296a84bbd2a5ab9bd8fc97 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 8 May 2018 14:40:12 -0700 Subject: [PATCH 191/280] console: Drop newline on glnx_console_lock This was inherited from some other code; perhaps the idea was to ensure the console is in a consistent state before starting a progress bar, but it causes extra newlines which is distracting. --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 88130745..c2fe29db 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -168,7 +168,7 @@ glnx_console_lock (GLnxConsoleRef *console) g_once_init_leave (&sigwinch_initialized, 1); } - { static const char initbuf[] = { '\n', 0x1B, 0x37 }; + { static const char initbuf[] = { 0x1B, 0x37 }; (void) fwrite (initbuf, 1, sizeof (initbuf), stdout); } } From 00ad6ee739d0343fb05be169e724a7dd71e0eb49 Mon Sep 17 00:00:00 2001 From: Matthew Leeds Date: Mon, 28 May 2018 20:20:52 -0700 Subject: [PATCH 192/280] Add g_autoptr support for GAsyncResult,GMount,GVolumeMonitor --- glnx-backport-autocleanups.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index cc249611..b5f3475d 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -82,6 +82,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref) /* Add GObject-based types as needed. */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref) @@ -96,6 +97,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMount, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref) @@ -105,6 +107,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVolumeMonitor, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibCompressor, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibDecompressor, g_object_unref) From 7be25521cf639acc77ad8f968eb6c918533e94ec Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 11 Jul 2018 14:56:32 -0400 Subject: [PATCH 193/280] libglnx.m4: Include stdio.h for renameat2 glibc added it upstream: https://sourceware.org/git/?p=glibc.git;a=commit;h=d6da5cb6a8e0e8a9ce92b7d951a254cf325248d7 But we need the right header. Ref: https://github.com/flatpak/flatpak/issues/1890 --- libglnx.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/libglnx.m4 b/libglnx.m4 index 34caf204..5922805b 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -7,6 +7,7 @@ AC_CHECK_DECLS([ [], [], [[ #include #include +#include #include #include #include From c2dbe1811bc5a0176d7df8280625220681a60011 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Fri, 30 Nov 2018 16:22:51 -0500 Subject: [PATCH 194/280] Fix docs for glnx_file_replace_contents() The docs for `glnx_file_replace_contents[_with_perms]` say that the default mode is 0666 - umask, but it's actually 0644. Because there's no thread-safe way of finding out the current umask without grubbing around in /proc/self/status, simply make the docs reflect reality. --- glnx-fdio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index a1f19037..7b734ff6 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1002,8 +1002,7 @@ glnx_file_copy_at (int src_dfd, * contents. This and other behavior can be controlled via @flags. * * Note that no metadata from the existing file is preserved, such as - * uid/gid or extended attributes. The default mode will be `0666`, - * modified by umask. + * uid/gid or extended attributes. The default mode will be `0644`. */ gboolean glnx_file_replace_contents_at (int dfd, @@ -1025,7 +1024,7 @@ glnx_file_replace_contents_at (int dfd, * @subpath: Subpath * @buf: (array len=len) (element-type guint8): File contents * @len: Length (if `-1`, assume @buf is `NUL` terminated) - * @mode: File mode; if `-1`, use `0666 - umask` + * @mode: File mode; if `-1`, use `0644` * @flags: Flags * @cancellable: Cancellable * @error: Error From 1e9b30838ea1101d7e3e924ddc58057842b8f3bf Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 30 Nov 2018 16:27:19 -0500 Subject: [PATCH 195/280] README: Update Contributing --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f2ae6ae3..6589d29b 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,7 @@ APIs not defined in GLib yet, such as `glnx_autofd`. Contributing ------------ -Currently there is not a Bugzilla product - one may be created -in the future. You can submit PRs against the Github mirror: +Development happens in GNOME Gitlab: https://gitlab.gnome.org/GNOME/libglnx -https://github.com/GNOME/libglnx/pulls - -Or alternatively, email one of the maintainers directly. +(If you're seeing this on the Github mirror, we used to do development + on Github but that was before GNOME deployed Gitlab.) From b1cb19b6b2d712b492e6376248f3010d18e59daa Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 6 Dec 2018 22:02:35 +0000 Subject: [PATCH 196/280] shutil: Prefix error with path in rm_rf() First, let's ensure the filename is prefixed consistently. Second, add the entrypoint as a prefix when recursing. This is best practice to help debugging. Motivated by https://discussion.fedoraproject.org/t/boot-partition-of-silverblue-is-without-space/771/9 --- glnx-shutil.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/glnx-shutil.c b/glnx-shutil.c index 75d05931..78042fe0 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -24,8 +24,23 @@ #include #include +#include #include +static gboolean +unlinkat_allow_noent (int dfd, + const char *path, + int flags, + GError **error) +{ + if (unlinkat (dfd, path, flags) == -1) + { + if (errno != ENOENT) + return glnx_throw_errno_prefix (error, "unlinkat(%s)", path); + } + return TRUE; +} + static gboolean glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, GCancellable *cancellable, @@ -51,16 +66,13 @@ glnx_shutil_rm_rf_children (GLnxDirFdIterator *dfd_iter, if (!glnx_shutil_rm_rf_children (&child_dfd_iter, cancellable, error)) return FALSE; - if (unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR) == -1) - return glnx_throw_errno_prefix (error, "unlinkat"); + if (!glnx_unlinkat (dfd_iter->fd, dent->d_name, AT_REMOVEDIR, error)) + return FALSE; } else { - if (unlinkat (dfd_iter->fd, dent->d_name, 0) == -1) - { - if (errno != ENOENT) - return glnx_throw_errno_prefix (error, "unlinkat"); - } + if (!unlinkat_allow_noent (dfd_iter->fd, dent->d_name, 0, error)) + return FALSE; } } @@ -86,7 +98,6 @@ glnx_shutil_rm_rf_at (int dfd, { dfd = glnx_dirfd_canonicalize (dfd); - /* With O_NOFOLLOW first */ glnx_autofd int target_dfd = openat (dfd, path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); @@ -100,8 +111,8 @@ glnx_shutil_rm_rf_at (int dfd, } else if (errsv == ENOTDIR || errsv == ELOOP) { - if (unlinkat (dfd, path, 0) != 0) - return glnx_throw_errno_prefix (error, "unlinkat"); + if (!glnx_unlinkat (dfd, path, 0, error)) + return FALSE; } else return glnx_throw_errno_prefix (error, "open(%s)", path); @@ -113,13 +124,10 @@ glnx_shutil_rm_rf_at (int dfd, return FALSE; if (!glnx_shutil_rm_rf_children (&dfd_iter, cancellable, error)) - return FALSE; + return glnx_prefix_error (error, "Removing %s", path); - if (unlinkat (dfd, path, AT_REMOVEDIR) == -1) - { - if (errno != ENOENT) - return glnx_throw_errno_prefix (error, "unlinkat"); - } + if (!unlinkat_allow_noent (dfd, path, AT_REMOVEDIR, error)) + return FALSE; } return TRUE; From 0f53dc35e4697bf1c545cb4eae795c79299e2cd0 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 28 Mar 2019 16:09:32 +0000 Subject: [PATCH 197/280] Add meson.build files --- meson.build | 83 +++++++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 19 +++++++++++ 2 files changed, 102 insertions(+) create mode 100644 meson.build create mode 100644 tests/meson.build diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..d9abf377 --- /dev/null +++ b/meson.build @@ -0,0 +1,83 @@ +project('libglnx', 'c') + +add_project_arguments('-D_GNU_SOURCE', language: 'c') + +cc = meson.get_compiler('c') + + +check_functions = [ + 'renameat2', + 'memfd_create', + 'copy_file_range', +] +conf = configuration_data() +foreach check_function : check_functions + if cc.compiles(''' + #include + #include + #include + #include + #include + #include + #include + #include + #include + + int func (void) { + (void) ''' + check_function + '''; + } + ''', + args : '-D_GNU_SOURCE', + name : check_function + '() is declared', + ) + conf.set('HAVE_DECL_' + check_function.underscorify().to_upper(), 1) + endif +endforeach +config_h = configure_file( + output : 'config.h', + configuration : conf, +) + +libglnx_deps = [ + dependency('gio-2.0'), + dependency('gio-unix-2.0'), +] +libglnx_inc = include_directories('.') +libglnx_sources = [ + 'glnx-backport-autocleanups.h', + 'glnx-backport-autoptr.h', + 'glnx-backports.c', + 'glnx-backports.h', + 'glnx-console.c', + 'glnx-console.h', + 'glnx-dirfd.c', + 'glnx-dirfd.h', + 'glnx-errors.c', + 'glnx-errors.h', + 'glnx-fdio.c', + 'glnx-fdio.h', + 'glnx-local-alloc.c', + 'glnx-local-alloc.h', + 'glnx-lockfile.c', + 'glnx-lockfile.h', + 'glnx-macros.h', + 'glnx-missing.h', + 'glnx-missing-syscall.h', + 'glnx-shutil.c', + 'glnx-shutil.h', + 'glnx-xattrs.c', + 'glnx-xattrs.h', + 'libglnx.h', +] + +libglnx = static_library('glnx', + libglnx_sources, + dependencies : libglnx_deps, + include_directories : libglnx_inc, + install : false) +libglnx_dep = declare_dependency( + include_directories : libglnx_inc, + link_with : libglnx) + +subdir('tests') + diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..d3eddf03 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,19 @@ + +test_names = [ + 'errors', + 'fdio', + 'macros', + 'shutil', + 'xattrs', +] + +foreach test_name : test_names + exe = executable(test_name, + ['test-libglnx-' + test_name + '.c', 'libglnx-testlib.h'], + dependencies: [ + libglnx_dep, + libglnx_deps, + ], + ) + test(test_name, exe) +endforeach From 80779a3dec43f468496a9ee19a4087ac46fc25e4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 1 May 2019 18:20:52 +0100 Subject: [PATCH 198/280] missing: Remove unused This doesn't exist on some very old platforms. In the original file in systemd, it was here for char32_t and char16_t, which we don't use. Signed-off-by: Simon McVittie --- glnx-missing.h | 1 - 1 file changed, 1 deletion(-) diff --git a/glnx-missing.h b/glnx-missing.h index 0eba07bf..9d35c40b 100644 --- a/glnx-missing.h +++ b/glnx-missing.h @@ -36,7 +36,6 @@ #include #include #include -#include #include /* The precise definition of __O_TMPFILE is arch specific; use the From 5920f9f3fc3c1c0fb029296b91938d103fb7886a Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 28 Mar 2019 16:12:39 +0000 Subject: [PATCH 199/280] Document using this as a Meson subproject --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 6589d29b..3be38d72 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,32 @@ One could also compare this project to gnulib; the salient differences there are that at least some of this module is eventually destined for inclusion in GLib. +Adding this to your project +--------------------------- + +## Meson + +First, set up a Git submodule: + +``` +git submodule add https://gitlab.gnome.org/GNOME/libglnx subprojects/libglnx +``` + +Or a Git [subtree](https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt): + +``` +git remote add libglnx https://gitlab.gnome.org/GNOME/libglnx.git +git fetch libglnx +git subtree add -P subprojects/libglnx libglnx/master +``` + +Then, in your top-level `meson.build`: + +``` +libglnx_dep = subproject('libglnx').get_variable('libglnx_dep') +# now use libglnx_dep in your dependencies +``` + Porting from libgsystem ----------------------- From 04c11c7390c5e044aa0079c3b5b5e7a3c8b7f577 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 2 May 2019 19:01:38 +0100 Subject: [PATCH 200/280] Run the fdio test in its own temporary directory The temporary directory will be deleted on success, but will remain intact on failure. Signed-off-by: Simon McVittie --- tests/libglnx-testlib.c | 66 +++++++++++++++++++++++++++++++++++++++ tests/libglnx-testlib.h | 14 +++++++++ tests/meson.build | 6 +++- tests/test-libglnx-fdio.c | 1 + 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 tests/libglnx-testlib.c diff --git a/tests/libglnx-testlib.c b/tests/libglnx-testlib.c new file mode 100644 index 00000000..5687d807 --- /dev/null +++ b/tests/libglnx-testlib.c @@ -0,0 +1,66 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright 2019 Collabora Ltd. + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include "libglnx-testlib.h" + +#include + +#include "libglnx.h" + +struct _GLnxTestAutoTempDir +{ + gchar *old_cwd; + int old_cwd_fd; + GLnxTmpDir temp_dir; +}; + +_GLnxTestAutoTempDir * +_glnx_test_auto_temp_dir_enter (void) +{ + GError *error = NULL; + _GLnxTestAutoTempDir *ret = g_new0 (_GLnxTestAutoTempDir, 1); + + glnx_mkdtemp ("glnx-test-XXXXXX", 0700, &ret->temp_dir, &error); + g_assert_no_error (error); + + /* just for better diagnostics */ + ret->old_cwd = g_get_current_dir (); + + glnx_opendirat (-1, ".", TRUE, &ret->old_cwd_fd, &error); + g_assert_no_error (error); + + if (fchdir (ret->temp_dir.fd) != 0) + g_error ("fchdir(): %s", ret->temp_dir.path, g_strerror (errno)); + + return ret; +} + +void +_glnx_test_auto_temp_dir_leave (_GLnxTestAutoTempDir *dir) +{ + GError *error = NULL; + + if (fchdir (dir->old_cwd_fd) != 0) + g_error ("fchdir(): %s", dir->old_cwd, g_strerror (errno)); + + glnx_tmpdir_delete (&dir->temp_dir, NULL, &error); + g_assert_no_error (error); +} diff --git a/tests/libglnx-testlib.h b/tests/libglnx-testlib.h index ee750e49..0d3a0751 100644 --- a/tests/libglnx-testlib.h +++ b/tests/libglnx-testlib.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2017 Red Hat, Inc. + * Copyright 2019 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,10 @@ #pragma once +#include + +#include "glnx-backport-autoptr.h" + typedef GError _GLnxTestAutoError; static inline void _glnx_test_auto_error_cleanup (_GLnxTestAutoError *autoerror) @@ -32,3 +37,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(_GLnxTestAutoError, _glnx_test_auto_error_cleanup) #define _GLNX_TEST_DECLARE_ERROR(local_error, error) \ g_autoptr(_GLnxTestAutoError) local_error = NULL; \ GError **error = &local_error + +typedef struct _GLnxTestAutoTempDir _GLnxTestAutoTempDir; + +_GLnxTestAutoTempDir *_glnx_test_auto_temp_dir_enter (void); +void _glnx_test_auto_temp_dir_leave (_GLnxTestAutoTempDir *dir); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_GLnxTestAutoTempDir, _glnx_test_auto_temp_dir_leave); + +#define _GLNX_TEST_SCOPED_TEMP_DIR \ + g_autoptr(_GLnxTestAutoTempDir) temp_dir = _glnx_test_auto_temp_dir_enter () diff --git a/tests/meson.build b/tests/meson.build index d3eddf03..de141c5a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -9,7 +9,11 @@ test_names = [ foreach test_name : test_names exe = executable(test_name, - ['test-libglnx-' + test_name + '.c', 'libglnx-testlib.h'], + [ + 'libglnx-testlib.c', + 'libglnx-testlib.h', + 'test-libglnx-' + test_name + '.c', + ], dependencies: [ libglnx_dep, libglnx_deps, diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 81636e59..84ebb14d 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -237,6 +237,7 @@ test_filecopy (void) int main (int argc, char **argv) { + _GLNX_TEST_SCOPED_TEMP_DIR; int ret; g_test_init (&argc, &argv, NULL); From 170b105b5f65ebb3a8409fde8ae7e4f55c384946 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 28 Mar 2019 16:09:42 +0000 Subject: [PATCH 201/280] Add GitLab CI This only uses Meson because it's not straightforward to do a standalone build with the Autotools goop. --- .gitlab-ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..0d204ba8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,22 @@ +image: fedora:rawhide + +stages: + - build + +before_script: + - dnf install -y gcc meson ninja-build "pkgconfig(gio-2.0)" "pkgconfig(gio-unix-2.0)" "pkgconfig(glib-2.0)" + +build: + stage: build + script: + - meson _build . + - cd _build + - ninja + - meson test + # Run it again! This previously did not work. + - meson test + artifacts: + when: on_failure + name: "libglnx-${CI_COMMIT_REF_NAME}-${CI_JOB_NAME}" + paths: + - "${CI_PROJECT_DIR}/_build/meson-logs" From 744bd1824f3a2202ddb4683856a8750dd3cfe24d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 10 May 2019 16:50:57 +0100 Subject: [PATCH 202/280] meson: Define HAVE_DECL_FOO to 0 if foo isn't declared This matches what Autotools would do, and what our header is expecting. It silences -Wundef. Signed-off-by: Simon McVittie --- meson.build | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index d9abf377..73d2bc55 100644 --- a/meson.build +++ b/meson.build @@ -12,7 +12,7 @@ check_functions = [ ] conf = configuration_data() foreach check_function : check_functions - if cc.compiles(''' + have_it = cc.compiles(''' #include #include #include @@ -30,8 +30,7 @@ foreach check_function : check_functions args : '-D_GNU_SOURCE', name : check_function + '() is declared', ) - conf.set('HAVE_DECL_' + check_function.underscorify().to_upper(), 1) - endif + conf.set10('HAVE_DECL_' + check_function.underscorify().to_upper(), have_it) endforeach config_h = configure_file( output : 'config.h', From 738ee7ace3d2530da1b608e224def929fb6cb986 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 2 May 2019 15:52:13 +0100 Subject: [PATCH 203/280] Make the Meson build work on older compilers Signed-off-by: Simon McVittie --- meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/meson.build b/meson.build index 73d2bc55..74ee36a3 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,8 @@ project('libglnx', 'c') add_project_arguments('-D_GNU_SOURCE', language: 'c') +add_project_arguments('-std=gnu99', language: 'c') +add_project_arguments('-Wno-unused-local-typedefs', language: 'c') cc = meson.get_compiler('c') From 4fca08e26808824b1f2a9866968eb245ce1da603 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 13 May 2019 19:02:12 +0100 Subject: [PATCH 204/280] CI: Target a Fedora stable release As suggested by @jlebon on libglnx!8. Signed-off-by: Simon McVittie --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d204ba8..36638d74 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: fedora:rawhide +image: registry.fedoraproject.org/fedora:30 stages: - build From 460400c19cfbf7bed2443b263d56094b7018e23a Mon Sep 17 00:00:00 2001 From: Alex Kiernan Date: Mon, 9 Sep 2019 14:19:18 +0100 Subject: [PATCH 205/280] macros: Add TEMP_FAILURE_RETRY for musl TEMP_FAILURE_RETRY is glibc specific, add a definition for musl. See https://github.com/ostreedev/ostree/issues/731 Signed-off-by: Alex Kiernan --- glnx-macros.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/glnx-macros.h b/glnx-macros.h index 6d8aca93..700fc75c 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -31,6 +31,16 @@ G_BEGIN_DECLS /* All of these are for C only. */ #ifndef __GI_SCANNER__ +/* fixes builds against musl, taken from glibc unistd.h */ +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + /* Taken from https://github.com/systemd/systemd/src/basic/string-util.h * at revision v228-666-gcf6c8c4 */ From e8233c6356ea5c27cf376c157344a1ff47843a57 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 9 Oct 2019 14:56:11 +0200 Subject: [PATCH 206/280] Add glnx_open_anonymous_tmpfile_full() allowing you to specify the directory This is useful if you need the file to be on a particular filesystem. In particular, flatpak wants this to make tempfiles on /tmp for things we need to write during flatpak run, such as the libseccomp output fd. We've had "flatpak run" stop working in low disk situations without this, so its nice to be able to fix it. --- glnx-fdio.c | 29 +++++++++++++++++++++++------ glnx-fdio.h | 7 +++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 7b734ff6..1dc5ffd1 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -277,17 +277,19 @@ glnx_open_tmpfile_linkable_at (int dfd, return open_tmpfile_core (dfd, subpath, flags, out_tmpf, error); } + /* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. - * Useful for true temporary storage. The fd will be allocated in /var/tmp to - * ensure maximum storage space. + * Useful for true temporary storage. The fd will be allocated in the specified + * directory. */ gboolean -glnx_open_anonymous_tmpfile (int flags, - GLnxTmpfile *out_tmpf, - GError **error) +glnx_open_anonymous_tmpfile_full (int flags, + const char *dir, + GLnxTmpfile *out_tmpf, + GError **error) { /* Add in O_EXCL */ - if (!open_tmpfile_core (AT_FDCWD, "/var/tmp", flags | O_EXCL, out_tmpf, error)) + if (!open_tmpfile_core (AT_FDCWD, dir, flags | O_EXCL, out_tmpf, error)) return FALSE; if (out_tmpf->path) { @@ -299,6 +301,21 @@ glnx_open_anonymous_tmpfile (int flags, return TRUE; } +/* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. + * Useful for true temporary storage. The fd will be allocated in /var/tmp to + * ensure maximum storage space. + * + * If you need the file on a specific filesystem use glnx_open_anonymous_tmpfile_full() + * which lets you pass a directory. + */ +gboolean +glnx_open_anonymous_tmpfile (int flags, + GLnxTmpfile *out_tmpf, + GError **error) +{ + return glnx_open_anonymous_tmpfile_full (flags, "/var/tmp", out_tmpf, error); +} + /* Use this after calling glnx_open_tmpfile_linkable_at() to give * the file its final name (link into place). */ diff --git a/glnx-fdio.h b/glnx-fdio.h index c0a7cc1d..d97ea366 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -83,6 +83,13 @@ glnx_open_anonymous_tmpfile (int flags, GLnxTmpfile *out_tmpf, GError **error); +gboolean +glnx_open_anonymous_tmpfile_full (int flags, + const char *dir, + GLnxTmpfile *out_tmpf, + GError **error); + + gboolean glnx_open_tmpfile_linkable_at (int dfd, const char *subpath, From 0cf0955acc5d27c86428e1dddae7652f267c2442 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 31 Oct 2019 12:20:00 +0000 Subject: [PATCH 207/280] fdio: Add glnx_tmpfile_reopen_rdonly() For fs-verity. --- glnx-fdio.c | 42 ++++++++++++++++++++++++++++++++++++++++-- glnx-fdio.h | 4 ++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 1dc5ffd1..12879cd7 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -363,8 +363,7 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, { /* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */ char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(tmpf->fd) + 1]; - - sprintf (proc_fd_path, "/proc/self/fd/%i", tmpf->fd); + snprintf (proc_fd_path, sizeof (proc_fd_path), "/proc/self/fd/%i", tmpf->fd); if (replace) { @@ -424,6 +423,45 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, return TRUE; } +/* glnx_tmpfile_reopen_rdonly: + * @tmpf: tmpfile + * @error: Error + * + * Give up write access to the file descriptior. One use + * case for this is fs-verity, which requires a read-only fd. + * It could also be useful to allocate an anonymous tmpfile + * write some sort of caching/indexing data to it, then reopen it + * read-only thereafter. + **/ +gboolean +glnx_tmpfile_reopen_rdonly (GLnxTmpfile *tmpf, + GError **error) +{ + g_return_val_if_fail (tmpf->fd >= 0, FALSE); + g_return_val_if_fail (tmpf->src_dfd == AT_FDCWD || tmpf->src_dfd >= 0, FALSE); + + glnx_fd_close int rdonly_fd = -1; + + if (tmpf->path) + { + if (!glnx_openat_rdonly (tmpf->src_dfd, tmpf->path, FALSE, &rdonly_fd, error)) + return FALSE; + } + else + { + /* This case we have O_TMPFILE, so our reference to it is via /proc/self/fd */ + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(tmpf->fd) + 1]; + snprintf (proc_fd_path, sizeof (proc_fd_path), "/proc/self/fd/%i", tmpf->fd); + + if (!glnx_openat_rdonly (AT_FDCWD, proc_fd_path, TRUE, &rdonly_fd, error)) + return FALSE; + } + + glnx_close_fd (&tmpf->fd); + tmpf->fd = glnx_steal_fd (&rdonly_fd); + return TRUE; +} + /** * glnx_openat_rdonly: * @dfd: File descriptor for origin directory diff --git a/glnx-fdio.h b/glnx-fdio.h index d97ea366..9c57dc5e 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -110,6 +110,10 @@ glnx_link_tmpfile_at (GLnxTmpfile *tmpf, const char *target, GError **error); +gboolean +glnx_tmpfile_reopen_rdonly (GLnxTmpfile *tmpf, + GError **error); + gboolean glnx_openat_rdonly (int dfd, const char *path, From 1503eaaac30edce9e6548170095813cb3e52acf5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 4 Nov 2019 22:17:58 +0000 Subject: [PATCH 208/280] build-sys: Add libglnx-testlib.c to Automake It was introduced in a previous commit but only added to `meson.build`. --- Makefile-libglnx.am | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 158063c7..957eae97 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -56,23 +56,25 @@ libglnx_la_LIBADD = $(libglnx_libs) libglnx_tests = test-libglnx-xattrs test-libglnx-fdio test-libglnx-errors test-libglnx-macros test-libglnx-shutil TESTS += $(libglnx_tests) +libglnx_testlib_sources = $(libglnx_srcpath)/tests/libglnx-testlib.c + check_PROGRAMS += $(libglnx_tests) -test_libglnx_xattrs_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-xattrs.c +test_libglnx_xattrs_SOURCES = $(libglnx_testlib_sources) $(libglnx_srcpath)/tests/test-libglnx-xattrs.c test_libglnx_xattrs_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_xattrs_LDADD = $(libglnx_libs) libglnx.la -test_libglnx_fdio_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-fdio.c +test_libglnx_fdio_SOURCES = $(libglnx_testlib_sources) $(libglnx_srcpath)/tests/test-libglnx-fdio.c test_libglnx_fdio_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_fdio_LDADD = $(libglnx_libs) libglnx.la -test_libglnx_errors_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-errors.c +test_libglnx_errors_SOURCES = $(libglnx_testlib_sources) $(libglnx_srcpath)/tests/test-libglnx-errors.c test_libglnx_errors_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_errors_LDADD = $(libglnx_libs) libglnx.la -test_libglnx_macros_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-macros.c +test_libglnx_macros_SOURCES = $(libglnx_testlib_sources) $(libglnx_srcpath)/tests/test-libglnx-macros.c test_libglnx_macros_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_macros_LDADD = $(libglnx_libs) libglnx.la -test_libglnx_shutil_SOURCES = $(libglnx_srcpath)/tests/test-libglnx-shutil.c +test_libglnx_shutil_SOURCES = $(libglnx_testlib_sources) $(libglnx_srcpath)/tests/test-libglnx-shutil.c test_libglnx_shutil_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) test_libglnx_shutil_LDADD = $(libglnx_libs) libglnx.la From 46ba6b4187ea7e70caeef4bee144a932c6bf84ca Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 1 May 2019 19:57:18 +0100 Subject: [PATCH 209/280] backports: Add a backport of g_clear_pointer() This will be necessary if targeting GLib versions older than 2.34, such as GLib 2.32 in Ubuntu 12.04 and the Steam Runtime. Signed-off-by: Simon McVittie --- glnx-backports.h | 20 ++++++++++++++++++++ glnx-fdio.c | 1 + 2 files changed, 21 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index cd853cca..b6d93c05 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -27,6 +27,26 @@ G_BEGIN_DECLS +#if !GLIB_CHECK_VERSION(2, 34, 0) +#define g_clear_pointer(pp, destroy) \ + G_STMT_START { \ + G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer)); \ + /* Only one access, please; work around type aliasing */ \ + union { char *in; gpointer *out; } _pp; \ + gpointer _p; \ + /* This assignment is needed to avoid a gcc warning */ \ + GDestroyNotify _destroy = (GDestroyNotify) (destroy); \ + \ + _pp.in = (char *) (pp); \ + _p = *_pp.out; \ + if (_p) \ + { \ + *_pp.out = NULL; \ + _destroy (_p); \ + } \ + } G_STMT_END +#endif + #if !GLIB_CHECK_VERSION(2, 44, 0) #define g_strv_contains glnx_strv_contains diff --git a/glnx-fdio.c b/glnx-fdio.c index 12879cd7..31bb15d2 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include From c8216c90ed8eedae574b01ac63449f6c9b3f7dda Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 1 May 2019 18:24:22 +0100 Subject: [PATCH 210/280] backport-autocleanups: Hide autocleanups for types we don't have If you're building on a really old GLib, you might not have GTask, GSubprocess or g_markup_parse_context_unref(), among others. This gets libglnx compiling (and apparently working) on GLib versions as old as 2.32. Signed-off-by: Simon McVittie --- glnx-backport-autocleanups.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index b5f3475d..50f469f2 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -49,7 +49,9 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref) +#if GLIB_CHECK_VERSION(2, 36, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref) +#endif G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free) @@ -75,11 +77,15 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free) +#if GLIB_CHECK_VERSION(2, 40, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear) +#endif G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free) +#if GLIB_CHECK_VERSION(2, 40, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref) +#endif /* Add GObject-based types as needed. */ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncResult, g_object_unref) @@ -101,7 +107,9 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMount, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref) +#if GLIB_CHECK_VERSION(2, 36, 0) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref) +#endif G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref) From 3063c69515dcda9129ebea989d4c95341f0c1e9f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 2 May 2019 15:51:42 +0100 Subject: [PATCH 211/280] Make tests cope with older GLib Signed-off-by: Simon McVittie --- glnx-backports.h | 12 ++++++++++++ tests/test-libglnx-xattrs.c | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index b6d93c05..6c39cf22 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -63,4 +63,16 @@ gboolean glnx_set_object (GObject **object_ptr, #endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ +#ifndef g_assert_nonnull +#define g_assert_nonnull(x) g_assert (x != NULL) +#endif + +#ifndef g_assert_null +#define g_assert_null(x) g_assert (x == NULL) +#endif + +#if !GLIB_CHECK_VERSION (2, 38, 0) +#define g_test_skip(s) g_test_message ("SKIP: %s", s) +#endif + G_END_DECLS diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 63e12314..82def4aa 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -218,7 +218,11 @@ test_xattr_races (void) /* FIXME - this deadlocks for me on 4.9.4-201.fc25.x86_64, whether * using overlayfs or xfs as source/dest. */ +#if GLIB_CHECK_VERSION (2, 36, 0) const guint nprocs = MAX (4, g_get_num_processors ()); +#else + const guint nprocs = 4; +#endif struct XattrWorker wdata[nprocs]; GThread *threads[nprocs]; g_autoptr(GError) local_error = NULL; From bc9c31f785ccb36b36ba1a53db1737d5cc7987c9 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 11 Nov 2019 18:34:48 +0000 Subject: [PATCH 212/280] _GLNX_TEST_SCOPED_TEMP_DIR: Mark variable as G_GNUC_UNUSED Otherwise, clang diagnoses it as unused. It is - deliberately - only allocated and cleaned up, with no other use. Signed-off-by: Simon McVittie --- tests/libglnx-testlib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libglnx-testlib.h b/tests/libglnx-testlib.h index 0d3a0751..d45ba860 100644 --- a/tests/libglnx-testlib.h +++ b/tests/libglnx-testlib.h @@ -45,4 +45,4 @@ void _glnx_test_auto_temp_dir_leave (_GLnxTestAutoTempDir *dir); G_DEFINE_AUTOPTR_CLEANUP_FUNC(_GLnxTestAutoTempDir, _glnx_test_auto_temp_dir_leave); #define _GLNX_TEST_SCOPED_TEMP_DIR \ - g_autoptr(_GLnxTestAutoTempDir) temp_dir = _glnx_test_auto_temp_dir_enter () + G_GNUC_UNUSED g_autoptr(_GLnxTestAutoTempDir) temp_dir = _glnx_test_auto_temp_dir_enter () From a652ede20b6bc4726fbbce41ea4657fc83d8c6bb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 11 Nov 2019 19:29:12 +0000 Subject: [PATCH 213/280] _GLNX_TEST_SCOPED_TEMP_DIR: Fix memory and fd leak This doesn't really matter, since it only happens when our process is about to exit anyway, but it makes it easier to use AddressSanitizer and similar tools. Signed-off-by: Simon McVittie --- tests/libglnx-testlib.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/libglnx-testlib.c b/tests/libglnx-testlib.c index 5687d807..869d144d 100644 --- a/tests/libglnx-testlib.c +++ b/tests/libglnx-testlib.c @@ -23,6 +23,8 @@ #include +#include + #include "libglnx.h" struct _GLnxTestAutoTempDir @@ -63,4 +65,10 @@ _glnx_test_auto_temp_dir_leave (_GLnxTestAutoTempDir *dir) glnx_tmpdir_delete (&dir->temp_dir, NULL, &error); g_assert_no_error (error); + + g_close (dir->old_cwd_fd, &error); + g_assert_no_error (error); + + g_free (dir->old_cwd); + g_free (dir); } From 70988f02872d5bb3a4123e6b98106acd2150f400 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Fri, 29 Nov 2019 21:05:14 +0000 Subject: [PATCH 214/280] README: update link to libgsystem I was curious. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3be38d72..587e5d47 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -libglnx is the successor to libgsystem: https://git.gnome.org/browse/libgsystem +libglnx is the successor to [libgsystem](https://gitlab.gnome.org/Archive/libgsystem). It is for modules which depend on both GLib and Linux, intended to be used as a git submodule. From dbed5c58db9db3331b2139f77e2b8a90642f0758 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 3 Jun 2020 17:51:19 +0100 Subject: [PATCH 215/280] fdio: Be const-correct for struct stat We don't modify this struct (if non-NULL), so it can be const. In particular, this is helpful if calling glnx_file_copy_at() from nftw() to implement the equivalent of `cp -a --reflink=auto`. Signed-off-by: Simon McVittie --- glnx-fdio.c | 2 +- glnx-fdio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 31bb15d2..6ae6ec7e 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -939,7 +939,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) gboolean glnx_file_copy_at (int src_dfd, const char *src_subpath, - struct stat *src_stbuf, + const struct stat *src_stbuf, int dest_dfd, const char *dest_subpath, GLnxFileCopyFlags copyflags, diff --git a/glnx-fdio.h b/glnx-fdio.h index 9c57dc5e..f95e473f 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -193,7 +193,7 @@ typedef enum { gboolean glnx_file_copy_at (int src_dfd, const char *src_subpath, - struct stat *src_stbuf, + const struct stat *src_stbuf, int dest_dfd, const char *dest_subpath, GLnxFileCopyFlags copyflags, From 3f38e98a3919c9e0e3e84e17cf8962d2d52ed73e Mon Sep 17 00:00:00 2001 From: Matthew Leeds Date: Thu, 11 Jun 2020 16:28:19 -0700 Subject: [PATCH 216/280] libglnx.m4: Fix grammar in help string --- libglnx.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libglnx.m4 b/libglnx.m4 index 5922805b..5a72e98b 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -25,10 +25,10 @@ AS_IF([test $enable_otmpfile = yes], [], [ AC_ARG_ENABLE(wrpseudo-compat, [AS_HELP_STRING([--enable-wrpseudo-compat], - [Disable use syscall() and filesystem calls to for compatibility with wrpseudo [default=no]])],, + [Disable use of syscall() in some cases for compatibility with pseudo [default=no]])],, [enable_wrpseudo_compat=no]) AS_IF([test $enable_wrpseudo_compat = no], [], [ - AC_DEFINE([ENABLE_WRPSEUDO_COMPAT], 1, [Define if we should be compatible with wrpseudo])]) + AC_DEFINE([ENABLE_WRPSEUDO_COMPAT], 1, [Define if we should be compatible with pseudo])]) dnl end LIBGLNX_CONFIGURE ]) From 7e3a19958e6af94b764c772a58adf47e6cc4b678 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 16 Jun 2020 12:29:46 -0400 Subject: [PATCH 217/280] glnx-fdio: handle EOPNOTSUPP for copy_file_range When using `copy_file_range` to target a source and dest on the same NFS mount on some older kernel versions, it's possible that we can get `EOPNOTSUPP` e.g. if the NFS server doesn't support server-side copy. We hit this in the FCOS release pipeline where we run `ostree pull-local` to pull content between two repos on the same mount from inside an OpenShift cluster on top of RHEL7. Nowadays, it seems like the kernel itself falls back to a more generic version of `copy_file_range()` at least. Though to be compatible with older kernels, let's add `EOPNOTSUPP` to the list of errors we interpret as "cfr possibly available, but can't be done for this specific operation". --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 6ae6ec7e..e537a9bc 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -826,7 +826,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) have_cfr = 0; try_cfr = false; } - else if (errno == EXDEV) + else if (G_IN_SET (errno, EXDEV, EOPNOTSUPP)) /* We won't try cfr again for this run, but let's be * conservative and not mark it as available/unavailable until * we know for sure. From de5d2ec85caf84f9ac7e3b2c84cbb849e8dfcd2b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 9 Sep 2020 12:33:35 +0000 Subject: [PATCH 218/280] xattrs: Add better error prefixing Related to https://github.com/coreos/coreos-assembler/issues/1703 This should help debug things like this in the future. --- glnx-xattrs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 79a14cd3..892d5343 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -283,7 +283,7 @@ set_all_xattrs_for_path (const char *path, const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1); if (lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0) - return glnx_throw_errno_prefix (error, "lsetxattr"); + return glnx_throw_errno_prefix (error, "lsetxattr(%s)", name); } return TRUE; @@ -351,7 +351,7 @@ glnx_fd_set_all_xattrs (int fd, const guint8* value_data = g_variant_get_fixed_array (value, &value_len, 1); if (TEMP_FAILURE_RETRY (fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0)) < 0) - return glnx_throw_errno_prefix (error, "fsetxattr"); + return glnx_throw_errno_prefix (error, "Setting xattrs: fsetxattr(%s)", name); } return TRUE; @@ -378,11 +378,11 @@ glnx_lgetxattrat (int dfd, ssize_t bytes_read, real_size; if (TEMP_FAILURE_RETRY (bytes_read = lgetxattr (pathbuf, attribute, NULL, 0)) < 0) - return glnx_null_throw_errno_prefix (error, "lgetxattr"); + return glnx_null_throw_errno_prefix (error, "lgetxattr(%s)", attribute); g_autofree guint8 *buf = g_malloc (bytes_read); if (TEMP_FAILURE_RETRY (real_size = lgetxattr (pathbuf, attribute, buf, bytes_read)) < 0) - return glnx_null_throw_errno_prefix (error, "lgetxattr"); + return glnx_null_throw_errno_prefix (error, "lgetxattr(%s)", attribute); return g_bytes_new_take (g_steal_pointer (&buf), real_size); } @@ -403,11 +403,11 @@ glnx_fgetxattr_bytes (int fd, ssize_t bytes_read, real_size; if (TEMP_FAILURE_RETRY (bytes_read = fgetxattr (fd, attribute, NULL, 0)) < 0) - return glnx_null_throw_errno_prefix (error, "fgetxattr"); + return glnx_null_throw_errno_prefix (error, "fgetxattr(%s)", attribute); g_autofree guint8 *buf = g_malloc (bytes_read); if (TEMP_FAILURE_RETRY (real_size = fgetxattr (fd, attribute, buf, bytes_read)) < 0) - return glnx_null_throw_errno_prefix (error, "fgetxattr"); + return glnx_null_throw_errno_prefix (error, "fgetxattr(%s)", attribute); return g_bytes_new_take (g_steal_pointer (&buf), real_size); } @@ -437,7 +437,7 @@ glnx_lsetxattrat (int dfd, snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); if (TEMP_FAILURE_RETRY (lsetxattr (subpath, attribute, value, len, flags)) < 0) - return glnx_throw_errno_prefix (error, "lsetxattr"); + return glnx_throw_errno_prefix (error, "lsetxattr(%s)", attribute); return TRUE; } From 1ea9158c24ab0c775c23403e853cc27bb0155d27 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Wed, 23 Sep 2020 13:33:43 -0400 Subject: [PATCH 219/280] glnx-fdio: try $TMPDIR if /var/tmp doesn't exist `glnx_open_anonymous_tmpfile` attempts to create an fd in `/var/tmp` regardless of the value of `$TMPDIR`. This is _usually_ okay, but can fail in some contexts, such as in the [NixOS][1] build environment, which doesn't have `/var` mapped at all. To avoid failing in this case, if the inner call to `glnx_open_anonymous_tmpfile_full` fails, we retrieve the value of `$TMPDIR` and try calling `glnx_open_anonymous_tmpfile_full` again with that directory instead. In the fast path (i.e. where `/var/tmp` exists), functionality is unchanged. [1]: https://nixos.org/ --- glnx-fdio.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index e537a9bc..9cf595e0 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -303,8 +303,8 @@ glnx_open_anonymous_tmpfile_full (int flags, } /* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. - * Useful for true temporary storage. The fd will be allocated in /var/tmp to - * ensure maximum storage space. + * Useful for true temporary storage. The fd will be allocated in `/var/tmp` + * (or `$TMPDIR` if `/var/tmp` doesn't exist) to ensure maximum storage space. * * If you need the file on a specific filesystem use glnx_open_anonymous_tmpfile_full() * which lets you pass a directory. @@ -314,7 +314,37 @@ glnx_open_anonymous_tmpfile (int flags, GLnxTmpfile *out_tmpf, GError **error) { - return glnx_open_anonymous_tmpfile_full (flags, "/var/tmp", out_tmpf, error); + if (glnx_open_anonymous_tmpfile_full (flags, "/var/tmp", out_tmpf, error)) + return TRUE; + else if ((*error)->code == G_IO_ERROR_NOT_FOUND) + { + /* If /var/tmp doesn't exist, such as in a NixOS build or + * other constrained systems, this will fail. + * + * Therefore, we try again using the directory in $TMPDIR if possible. + */ + const char *tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + return FALSE; + + GError *tmp_error; + tmp_error = NULL; + if (!glnx_open_anonymous_tmpfile_full (flags, tmpdir, + out_tmpf, &tmp_error)) + { + g_propagate_error (error, tmp_error); + return FALSE; + } + else + { + // Don't leave the error from the first call to + // glnx_open_anonymous_tmpfile_full in `error`. + g_clear_error(error); + return TRUE; + } + } + else + return FALSE; } /* Use this after calling glnx_open_tmpfile_linkable_at() to give From a5b76690b1c351cee5aede6e1a476bd955d1b00c Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 1 Oct 2020 10:37:47 -0400 Subject: [PATCH 220/280] glnx-fdio: use $TMPDIR if set As suggested by Colin Walters. --- glnx-fdio.c | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 9cf595e0..422bc2dd 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -303,8 +303,8 @@ glnx_open_anonymous_tmpfile_full (int flags, } /* A variant of `glnx_open_tmpfile_linkable_at()` which doesn't support linking. - * Useful for true temporary storage. The fd will be allocated in `/var/tmp` - * (or `$TMPDIR` if `/var/tmp` doesn't exist) to ensure maximum storage space. + * Useful for true temporary storage. The fd will be allocated in `$TMPDIR` if + * set or `/var/tmp` otherwise. * * If you need the file on a specific filesystem use glnx_open_anonymous_tmpfile_full() * which lets you pass a directory. @@ -314,37 +314,10 @@ glnx_open_anonymous_tmpfile (int flags, GLnxTmpfile *out_tmpf, GError **error) { - if (glnx_open_anonymous_tmpfile_full (flags, "/var/tmp", out_tmpf, error)) - return TRUE; - else if ((*error)->code == G_IO_ERROR_NOT_FOUND) - { - /* If /var/tmp doesn't exist, such as in a NixOS build or - * other constrained systems, this will fail. - * - * Therefore, we try again using the directory in $TMPDIR if possible. - */ - const char *tmpdir = getenv("TMPDIR"); - if (tmpdir == NULL) - return FALSE; - - GError *tmp_error; - tmp_error = NULL; - if (!glnx_open_anonymous_tmpfile_full (flags, tmpdir, - out_tmpf, &tmp_error)) - { - g_propagate_error (error, tmp_error); - return FALSE; - } - else - { - // Don't leave the error from the first call to - // glnx_open_anonymous_tmpfile_full in `error`. - g_clear_error(error); - return TRUE; - } - } - else - return FALSE; + return glnx_open_anonymous_tmpfile_full (flags, + getenv("TMPDIR") ?: "/var/tmp", + out_tmpf, + error); } /* Use this after calling glnx_open_tmpfile_linkable_at() to give From 013417ea72aa767aec15259271ef04846070be64 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 2 Nov 2020 14:37:26 +0100 Subject: [PATCH 221/280] Add GLNX_FILE_REPLACE_INCREASING_MTIME This make replaced files have a strictly increasing st_mtime. The main usecase I have for this is to ensure the summary file mtime increases because the flatpak tests are failing due to the python httpd used in the tests rely on st_mtime for the http If-Modified-Since header. For the tests this breaks all the time since we're just doing a lot of summary updates. However, I can see this accidentally happening in the wild too, so i think its proper to always ensure the new summary is "newer", even though it means it will be timestamped slightly in the future. In practice this will not happen regularly, and the times it *does* happen we really do need it. --- glnx-fdio.c | 44 +++++++++++++++++++++++++++++++++++--------- glnx-fdio.h | 2 ++ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 422bc2dd..d4eeb24c 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1105,6 +1105,11 @@ glnx_file_replace_contents_with_perms_at (int dfd, { char *dnbuf = strdupa (subpath); const char *dn = dirname (dnbuf); + gboolean increasing_mtime = (flags & GLNX_FILE_REPLACE_INCREASING_MTIME) != 0; + gboolean nodatasync = (flags & GLNX_FILE_REPLACE_NODATASYNC) != 0; + gboolean datasync_new = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) != 0; + struct stat stbuf; + gboolean has_stbuf = FALSE; dfd = glnx_dirfd_canonicalize (dfd); @@ -1128,34 +1133,55 @@ glnx_file_replace_contents_with_perms_at (int dfd, if (glnx_loop_write (tmpf.fd, buf, len) < 0) return glnx_throw_errno_prefix (error, "write"); - if (!(flags & GLNX_FILE_REPLACE_NODATASYNC)) + if (!nodatasync || increasing_mtime) { - struct stat stbuf; - gboolean do_sync; - if (!glnx_fstatat_allow_noent (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW, error)) return FALSE; - if (errno == ENOENT) - do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0; + has_stbuf = errno != ENOENT; + } + + if (!nodatasync) + { + gboolean do_sync; + if (!has_stbuf) + do_sync = datasync_new; else do_sync = TRUE; if (do_sync) { - if (fdatasync (tmpf.fd) != 0) + if (TEMP_FAILURE_RETRY (fdatasync (tmpf.fd)) != 0) return glnx_throw_errno_prefix (error, "fdatasync"); } } if (uid != (uid_t) -1) { - if (fchown (tmpf.fd, uid, gid) != 0) + if (TEMP_FAILURE_RETRY (fchown (tmpf.fd, uid, gid)) != 0) return glnx_throw_errno_prefix (error, "fchown"); } - if (fchmod (tmpf.fd, mode) != 0) + if (TEMP_FAILURE_RETRY (fchmod (tmpf.fd, mode)) != 0) return glnx_throw_errno_prefix (error, "fchmod"); + if (increasing_mtime && has_stbuf) + { + struct stat fd_stbuf; + + if (fstat (tmpf.fd, &fd_stbuf) != 0) + return glnx_throw_errno_prefix (error, "fstat"); + + /* We want to ensure that the new file has a st_mtime (i.e. the second precision) + * is incrementing to avoid mtime check issues when files change often. + */ + if (fd_stbuf.st_mtime <= stbuf.st_mtime) + { + struct timespec ts[2] = { {0, UTIME_OMIT}, {stbuf.st_mtime + 1, 0} }; + if (TEMP_FAILURE_RETRY (futimens (tmpf.fd, ts)) != 0) + return glnx_throw_errno_prefix (error, "futimens"); + } + } + if (!glnx_link_tmpfile_at (&tmpf, GLNX_LINK_TMPFILE_REPLACE, dfd, subpath, error)) return FALSE; diff --git a/glnx-fdio.h b/glnx-fdio.h index f95e473f..cc1ed4b1 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -143,12 +143,14 @@ glnx_file_get_contents_utf8_at (int dfd, * GLnxFileReplaceFlags: * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync() + * @GLNX_FILE_REPLACE_INCREASING_MTIME: Ensure that st_mtime increases (in second precision) * * Flags controlling file replacement. */ typedef enum { GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0), GLNX_FILE_REPLACE_NODATASYNC = (1 << 1), + GLNX_FILE_REPLACE_INCREASING_MTIME = (1 << 2), } GLnxFileReplaceFlags; gboolean From efcacc4c5fc8a94d3a202c6c6549c155a14600e4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 22 Nov 2020 19:06:14 +0000 Subject: [PATCH 222/280] fdio: Fix compilation via C++ I may use https://cxx.rs/ in rpm-ostree, which means we need our dependencies' headers to build in C++ mode. --- glnx-fdio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index cc1ed4b1..40931bfb 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -43,7 +43,7 @@ G_BEGIN_DECLS static inline const char *glnx_basename (const char *path) { - gchar *base = strrchr (path, G_DIR_SEPARATOR); + const gchar *base = strrchr (path, G_DIR_SEPARATOR); if (base) return base + 1; @@ -55,7 +55,7 @@ const char *glnx_basename (const char *path) static inline void glnx_stdio_file_cleanup (void *filep) { - FILE *f = filep; + FILE *f = (FILE*)filep; if (f) fclose (f); } From a8b4418954bf200746dc5c5cc450649cc345cbd2 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 22 Nov 2020 22:59:45 +0000 Subject: [PATCH 223/280] glnx_strjoina: Cast to result for C++ compatibility Prep for using C++ in rpm-ostree (temporarily). --- glnx-macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-macros.h b/glnx-macros.h index 700fc75c..7b3afd4f 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -52,7 +52,7 @@ G_BEGIN_DECLS unsigned _i_; \ for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ _len_ += strlen(_appendees_[_i_]); \ - _p_ = _d_ = alloca(_len_ + 1); \ + _p_ = _d_ = (char*) alloca(_len_ + 1); \ for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \ _p_ = stpcpy(_p_, _appendees_[_i_]); \ *_p_ = 0; \ From 900caea698690b18db4f2a9cd2c3abb4f84f10b5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 6 Dec 2020 20:29:55 +0000 Subject: [PATCH 224/280] macros: Fix casting for C++ compatibility Prep for more work on porting rpm-ostree to use cxx-rs. --- glnx-macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-macros.h b/glnx-macros.h index 7b3afd4f..3177bb66 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -136,7 +136,7 @@ G_BEGIN_DECLS guard && ({ g_hash_table_iter_init (&it, ht), TRUE; }); \ guard = FALSE) \ for (kt k; guard; guard = FALSE) \ - for (vt v; g_hash_table_iter_next (&it, (gpointer)&k, (gpointer)&v);) + for (vt v; g_hash_table_iter_next (&it, (void**)&k, (void**)&v);) /* Cleaner method to iterate over a GHashTable. I.e. rather than From 1345882d6a5fc3ea851bfe5510e79861d511c25e Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 9 Feb 2021 16:08:02 -0500 Subject: [PATCH 225/280] glnx_file_copy_at: Add GLNX_FILE_COPY_NOCHOWN In some contexts, we may want to copy a root-owned file but we're not running as root so we can't `fchown` it. (The case I'm interested in is actually a bit more obscure than this: running in a supermin VM as root, and wanting to copy a file we created onto a 9p mount where we don't have perms to `fchown`). Add a `GLNX_FILE_COPY_NOCHOWN` to handle this case. --- glnx-fdio.c | 7 +++++-- glnx-fdio.h | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index d4eeb24c..3fa73b5d 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1000,8 +1000,11 @@ glnx_file_copy_at (int src_dfd, if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0) return glnx_throw_errno_prefix (error, "regfile copy"); - if (fchown (tmp_dest.fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) - return glnx_throw_errno_prefix (error, "fchown"); + if (!(copyflags & GLNX_FILE_COPY_NOCHOWN)) + { + if (fchown (tmp_dest.fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0) + return glnx_throw_errno_prefix (error, "fchown"); + } if (!(copyflags & GLNX_FILE_COPY_NOXATTRS)) { diff --git a/glnx-fdio.h b/glnx-fdio.h index 40931bfb..3d1f024c 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -189,7 +189,8 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes); typedef enum { GLNX_FILE_COPY_OVERWRITE = (1 << 0), GLNX_FILE_COPY_NOXATTRS = (1 << 1), - GLNX_FILE_COPY_DATASYNC = (1 << 2) + GLNX_FILE_COPY_DATASYNC = (1 << 2), + GLNX_FILE_COPY_NOCHOWN = (1 << 3) } GLnxFileCopyFlags; gboolean From a0750a00b1b2282c9db6becf9a9664e509c84ba3 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 22 Apr 2021 15:40:51 +0100 Subject: [PATCH 226/280] glnx-dirfd: Add a rewinddir() wrapper This is useful in the same situations rewinddir() is. My use-case right now is to remove some of the files from a directory, then go back through the directory removing symlinks that pointed to those files. Signed-off-by: Simon McVittie --- glnx-dirfd.c | 18 ++++++++++++++++++ glnx-dirfd.h | 1 + 2 files changed, 19 insertions(+) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 6d1e2d21..0a63bcbc 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -173,6 +173,24 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, return TRUE; } +/** + * glnx_dirfd_iterator_rewind: + * @dfd_iter: A directory iterator + * + * Rewind to the beginning of @dfd_iter. The next call to + * glnx_dirfd_iterator_next_dent() will provide the first entry in the + * directory. + */ +void +glnx_dirfd_iterator_rewind (GLnxDirFdIterator *dfd_iter) +{ + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + + g_return_if_fail (dfd_iter->initialized); + + rewinddir (real_dfd_iter->d); +} + /** * glnx_dirfd_iterator_next_dent_ensure_dtype: * @dfd_iter: A directory iterator diff --git a/glnx-dirfd.h b/glnx-dirfd.h index 0046ac8e..d5773e72 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -66,6 +66,7 @@ gboolean glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_ite struct dirent **out_dent, GCancellable *cancellable, GError **error); +void glnx_dirfd_iterator_rewind (GLnxDirFdIterator *dfd_iter); void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear) From 62f8ed6c8ef489201a19f1e28656c58b363ba851 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 28 Jun 2021 10:05:09 -0400 Subject: [PATCH 227/280] console: Pacify `gcc -fanalyzer` It's not smart enough to follow the conditionals here to see that `textlen > 0` implies `text != NULL`. But the cost of an additional check is probably nil, so let's be robust to future code refactoring. --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index c2fe29db..0a96cdce 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -261,7 +261,7 @@ text_percent_internal (const char *text, const guint textlen = MIN (input_textlen, ncolumns - bar_min); const guint barlen = MIN (MAX_PROGRESSBAR_COLUMNS, ncolumns - (textlen + 1)); - if (textlen > 0) + if (text && textlen > 0) { fwrite (text, 1, textlen, stdout); fputc (' ', stdout); From 1f02e4313a98a972d41e948f4cecba9c664b20ab Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 28 Jun 2021 15:53:08 +0100 Subject: [PATCH 228/280] glnx-lockfile.h: Remove unused inclusion of config.h Signed-off-by: Simon McVittie --- glnx-lockfile.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/glnx-lockfile.h b/glnx-lockfile.h index b3465089..0adfeac1 100644 --- a/glnx-lockfile.h +++ b/glnx-lockfile.h @@ -22,8 +22,6 @@ along with systemd; If not, see . ***/ -#include "config.h" - #include "glnx-backport-autoptr.h" typedef struct GLnxLockFile { From c306703c6fb4b52b8ef1ebfdeb1b234fef8665a0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Apr 2021 18:08:55 +0100 Subject: [PATCH 229/280] Include libglnx-config.h instead of config.h This avoids colliding with a config.h generated by a parent Meson project. In the Meson build system, we generate libglnx-config.h by doing our own checks, so we want to avoid it colliding. In the Autotools build system, we assume that the parent project will generate its own config.h that contains the results of LIBGLNX_CONFIGURE, and create a forwarding header libglnx-config.h in the $(top_builddir) (so that it is next to config.h). Note that after updating libglnx in an Autotools non-recursive-Make project (libostree, flatpak, flatpak-builder) it will be necessary to re-run autogen.sh. Signed-off-by: Simon McVittie --- .gitignore | 2 ++ Makefile-libglnx.am | 6 +++++- glnx-backports.c | 2 +- glnx-console.c | 2 +- glnx-dirfd.c | 2 +- glnx-errors.c | 2 +- glnx-fdio.c | 2 +- glnx-local-alloc.c | 2 +- glnx-lockfile.c | 2 +- glnx-missing-syscall.h | 2 +- glnx-shutil.c | 2 +- glnx-xattrs.c | 2 +- meson.build | 2 +- tests/libglnx-testlib.c | 2 +- tests/test-libglnx-errors.c | 2 +- tests/test-libglnx-fdio.c | 2 +- tests/test-libglnx-macros.c | 2 +- tests/test-libglnx-shutil.c | 2 +- tests/test-libglnx-xattrs.c | 2 +- 19 files changed, 24 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 55b946e7..65a2e4da 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ # subdir-objects Makefile-libglnx.am.inc +libglnx-config.h + # Some standard bits .deps .libs diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 957eae97..b09e43bf 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -21,6 +21,10 @@ EXTRA_DIST += \ $(libglnx_srcpath)/libglnx.m4 \ $(NULL) +BUILT_SOURCES += $(top_builddir)/libglnx-config.h +$(top_builddir)/libglnx-config.h: Makefile.am + echo '#include "config.h"' > $@ + libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-macros.h \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ @@ -49,7 +53,7 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/tests/libglnx-testlib.h \ $(NULL) -libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) +libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags) -I$(builddir) libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic libglnx_la_LIBADD = $(libglnx_libs) diff --git a/glnx-backports.c b/glnx-backports.c index c7bb600f..c016d37e 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "glnx-backports.h" diff --git a/glnx-console.c b/glnx-console.c index c2fe29db..12976b35 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "glnx-console.h" diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 0a63bcbc..041e1cfe 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include diff --git a/glnx-errors.c b/glnx-errors.c index f350f305..52a61619 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include #include diff --git a/glnx-fdio.c b/glnx-fdio.c index 3fa73b5d..a3eeb80e 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -21,7 +21,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include #include diff --git a/glnx-local-alloc.c b/glnx-local-alloc.c index 692f0de2..58776b94 100644 --- a/glnx-local-alloc.c +++ b/glnx-local-alloc.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "glnx-local-alloc.h" diff --git a/glnx-lockfile.c b/glnx-lockfile.c index f1d52dee..4f7e8c78 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -22,7 +22,7 @@ along with systemd; If not, see . ***/ -#include "config.h" +#include "libglnx-config.h" #include #include diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index 4876ca39..0b617bda 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -30,7 +30,7 @@ Add abstraction model for BPF programs */ -#include "config.h" +#include "libglnx-config.h" #if !HAVE_DECL_RENAMEAT2 # ifndef __NR_renameat2 diff --git a/glnx-shutil.c b/glnx-shutil.c index 78042fe0..8aa4f21c 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 892d5343..92677ef1 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include #include diff --git a/meson.build b/meson.build index 74ee36a3..6c2dbf26 100644 --- a/meson.build +++ b/meson.build @@ -35,7 +35,7 @@ foreach check_function : check_functions conf.set10('HAVE_DECL_' + check_function.underscorify().to_upper(), have_it) endforeach config_h = configure_file( - output : 'config.h', + output : 'libglnx-config.h', configuration : conf, ) diff --git a/tests/libglnx-testlib.c b/tests/libglnx-testlib.c index 869d144d..879b8766 100644 --- a/tests/libglnx-testlib.c +++ b/tests/libglnx-testlib.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx-testlib.h" #include diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 4e91e02c..9e8c1e3b 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx.h" #include #include diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 84ebb14d..fcf0ef81 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx.h" #include #include diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index ffde8fae..68dc5db2 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx.h" #include #include diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index 6917b890..b4ee838c 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx.h" #include #include diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 82def4aa..0789a2ec 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -18,7 +18,7 @@ * Boston, MA 02111-1307, USA. */ -#include "config.h" +#include "libglnx-config.h" #include "libglnx.h" #include #include From a4dcfe8d2f81ee8d90570ab5593e3c0947d52371 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 2 Jul 2021 14:19:15 +0100 Subject: [PATCH 230/280] build: Use Meson built-in option c_std instead of -std=gnu99 This silences a Meson warning. Signed-off-by: Simon McVittie --- meson.build | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 74ee36a3..cecb0606 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,12 @@ -project('libglnx', 'c') +project( + 'libglnx', + 'c', + default_options : [ + 'c_std=gnu99', + ], +) add_project_arguments('-D_GNU_SOURCE', language: 'c') -add_project_arguments('-std=gnu99', language: 'c') add_project_arguments('-Wno-unused-local-typedefs', language: 'c') cc = meson.get_compiler('c') From 82e5b7dd568f4bfd8a77b809dd2c067e0a91e892 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 3 Aug 2021 16:38:34 -0400 Subject: [PATCH 231/280] xattrs: Fix possible double-free in `get_xattrs_impl` When we free `xattr_name`, we need to transfer ownership away to avoid potential for a double-free if we hit `ENOTSUP` on the next iteration. Reported-by: Seth Arnold --- glnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 892d5343..785ad0e4 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -181,7 +181,7 @@ get_xattrs_impl (const char *path, { if (errno == ERANGE) { - g_free (xattr_names); + g_free (g_steal_pointer (&xattr_names)); goto again; } glnx_set_prefix_error_from_errno (error, "%s", "llistxattr"); From 24231a956a4b849087fbf01173cdebb53e1bd60b Mon Sep 17 00:00:00 2001 From: Olaf Leidinger Date: Mon, 24 Jan 2022 16:12:57 +0100 Subject: [PATCH 232/280] Fall back if copy_file_range fails with EINVAL Although EINVAL usually indicates a programming error, ecryptfs (and possibly other stacked filesystems) returns EINVAL for attempts to copy_file_range() or sendfile() between files on that filesystem. Resolves: https://gitlab.gnome.org/GNOME/libglnx/-/issues/3 --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 3fa73b5d..fb572807 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -829,7 +829,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) have_cfr = 0; try_cfr = false; } - else if (G_IN_SET (errno, EXDEV, EOPNOTSUPP)) + else if (G_IN_SET (errno, EXDEV, EINVAL, EOPNOTSUPP)) /* We won't try cfr again for this run, but let's be * conservative and not mark it as available/unavailable until * we know for sure. From 586bdfe1c316ce5e129978d266c0f532e9d6ce5a Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 28 Jan 2022 11:39:31 +0000 Subject: [PATCH 233/280] fdio: Use a read/write loop until EOF if st_size is zero Some pseudo-files in /proc and /sys list a size of 0 bytes in stat() results, but do in fact have content. For these pseudo-files, the only way to find out the true file size is to try read() until EOF, so leave the max_bytes set to -1. copy_file_range() and sendfile() don't work for such files in some kernel versions (see ), so skip the fast-paths and use a read() and write() loop. For pseudo-files, we expect to be able to copy the whole content in one read() in any case. Signed-off-by: Simon McVittie --- glnx-fdio.c | 8 ++++--- tests/test-libglnx-fdio.c | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 3fa73b5d..c6d130b4 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -805,7 +805,9 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) */ if (fstat (fdf, &stbuf) < 0) return -1; - max_bytes = stbuf.st_size; + + if (stbuf.st_size > 0) + max_bytes = stbuf.st_size; } while (TRUE) @@ -816,7 +818,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) * try_copy_file_range() from systemd upstream, which works better since * we use POSIX errno style. */ - if (try_cfr) + if (try_cfr && max_bytes != (off_t) -1) { n = copy_file_range (fdf, NULL, fdt, NULL, max_bytes, 0u); if (n < 0) @@ -855,7 +857,7 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) /* Next try sendfile(); this version is also changed from systemd upstream * to match the same logic we have for copy_file_range(). */ - if (try_sendfile) + if (try_sendfile && max_bytes != (off_t) -1) { n = sendfile (fdt, fdf, NULL, max_bytes); if (n < 0) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 84ebb14d..be4448f7 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -235,6 +235,54 @@ test_filecopy (void) g_assert (S_ISREG (stbuf.st_mode)); } +static void +test_filecopy_procfs (void) +{ + const char * const pseudo_files[] = + { + /* A file in /proc that stat()s as empty (at least on Linux 5.15) */ + "/proc/version", + /* A file in /sys that stat()s as empty (at least on Linux 5.15) */ + "/sys/fs/cgroup/cgroup.controllers", + /* A file in /sys that stat()s as non-empty (at least on Linux 5.15) */ + "/sys/fs/ext4/features/meta_bg_resize", + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS (pseudo_files); i++) + { + _GLNX_TEST_DECLARE_ERROR(local_error, error); + g_autofree char *contents = NULL; + g_autofree char *contents_of_copy = NULL; + gsize len; + gsize len_copy; + + if (!g_file_get_contents (pseudo_files[i], &contents, &len, error)) + { + g_test_message ("Not testing %s: %s", + pseudo_files[i], local_error->message); + g_clear_error (&local_error); + continue; + } + + if (!glnx_file_copy_at (AT_FDCWD, pseudo_files[i], NULL, + AT_FDCWD, "copy", + GLNX_FILE_COPY_OVERWRITE | GLNX_FILE_COPY_NOCHOWN, + NULL, error)) + return; + + g_assert_no_error (local_error); + + if (!g_file_get_contents ("copy", &contents_of_copy, &len_copy, error)) + return; + + g_assert_no_error (local_error); + + g_assert_cmpstr (contents, ==, contents_of_copy); + g_assert_cmpuint (len, ==, len_copy); + } +} + int main (int argc, char **argv) { _GLNX_TEST_SCOPED_TEMP_DIR; @@ -245,6 +293,7 @@ int main (int argc, char **argv) g_test_add_func ("/tmpfile", test_tmpfile); g_test_add_func ("/stdio-file", test_stdio_file); g_test_add_func ("/filecopy", test_filecopy); + g_test_add_func ("/filecopy-procfs", test_filecopy_procfs); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); g_test_add_func ("/fstat", test_fstatat); From 81c6adb0f7dce9e790086bf353def911fc840464 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 2 Jul 2021 12:14:11 +0100 Subject: [PATCH 234/280] Declare copyright and licensing using REUSE To fill in some gaps, I've had to make some assumptions: * trivial changes (such as checking for an additional function or header file in libglnx.m4) are assumed to not be copyrightable * Will Thompson and Matthew Leeds are assumed to be contributing on behalf of Endless Mobile Inc. * files with no explicit licensing information are assumed to be under the license found in COPYING Reference: https://reuse.software/ Signed-off-by: Simon McVittie --- .gitignore | 3 + .gitlab-ci.yml | 3 + COPYING | 505 +-------------------------------- LICENSES/LGPL-2.0-or-later.txt | 174 ++++++++++++ LICENSES/LGPL-2.1-or-later.txt | 502 ++++++++++++++++++++++++++++++++ Makefile-libglnx.am | 3 + README.md | 6 + glnx-backport-autocleanups.h | 1 + glnx-backport-autoptr.h | 1 + glnx-backports.c | 1 + glnx-backports.h | 1 + glnx-console.c | 1 + glnx-console.h | 1 + glnx-dirfd.c | 1 + glnx-dirfd.h | 1 + glnx-errors.c | 1 + glnx-errors.h | 1 + glnx-fdio.c | 1 + glnx-fdio.h | 1 + glnx-local-alloc.c | 1 + glnx-local-alloc.h | 1 + glnx-lockfile.c | 1 + glnx-lockfile.h | 1 + glnx-macros.h | 1 + glnx-missing-syscall.h | 1 + glnx-missing.h | 1 + glnx-shutil.c | 1 + glnx-shutil.h | 1 + glnx-xattrs.c | 1 + glnx-xattrs.h | 1 + libglnx.doap | 4 + libglnx.h | 1 + libglnx.m4 | 4 + meson.build | 4 + tests/libglnx-testlib.c | 1 + tests/libglnx-testlib.h | 1 + tests/meson.build | 3 + tests/test-libglnx-errors.c | 1 + tests/test-libglnx-fdio.c | 1 + tests/test-libglnx-macros.c | 1 + tests/test-libglnx-shutil.c | 1 + tests/test-libglnx-xattrs.c | 1 + 42 files changed, 740 insertions(+), 502 deletions(-) create mode 100644 LICENSES/LGPL-2.0-or-later.txt create mode 100644 LICENSES/LGPL-2.1-or-later.txt diff --git a/.gitignore b/.gitignore index 65a2e4da..f75435d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Copyright 2015 Colin Walters +# SPDX-License-Identifier: LGPL-2.1-or-later + # A path ostree writes to work around automake bug with # subdir-objects Makefile-libglnx.am.inc diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 36638d74..9fa25bc5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,6 @@ +# Copyright 2019 Endless Mobile, Inc. +# SPDX-License-Identifier: LGPL-2.0-or-later + image: registry.fedoraproject.org/fedora:30 stages: diff --git a/COPYING b/COPYING index 4362b491..cb402477 100644 --- a/COPYING +++ b/COPYING @@ -1,502 +1,3 @@ - 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! +This project's licensing is REUSE-compliant . +See individual files for full details of copyright and licensing, +and see LICENSES/*.txt for the license text. diff --git a/LICENSES/LGPL-2.0-or-later.txt b/LICENSES/LGPL-2.0-or-later.txt new file mode 100644 index 00000000..eb3a4cd1 --- /dev/null +++ b/LICENSES/LGPL-2.0-or-later.txt @@ -0,0 +1,174 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1991 Free Software Foundation, Inc. +51 Franklin St, 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 library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] + +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 Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 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 a program 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. + +Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, 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 companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. + +The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. + +However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. + +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, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library 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 compile 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) 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. + + c) 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. + + d) 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 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. + +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 to 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 Library 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. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, 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. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 00000000..4362b491 --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,502 @@ + 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! diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index b09e43bf..9d1faa99 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -1,4 +1,5 @@ # Copyright (C) 2015 Colin Walters +# SPDX-License-Identifier: LGPL-2.0-or-later # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,6 +19,8 @@ EXTRA_DIST += \ $(libglnx_srcpath)/README.md \ $(libglnx_srcpath)/COPYING \ + $(libglnx_srcpath)/LICENSES/LGPL-2.0-or-later.txt \ + $(libglnx_srcpath)/LICENSES/LGPL-2.1-or-later.txt \ $(libglnx_srcpath)/libglnx.m4 \ $(NULL) diff --git a/README.md b/README.md index 587e5d47..f1e0b5c4 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,9 @@ Development happens in GNOME Gitlab: https://gitlab.gnome.org/GNOME/libglnx (If you're seeing this on the Github mirror, we used to do development on Github but that was before GNOME deployed Gitlab.) + + diff --git a/glnx-backport-autocleanups.h b/glnx-backport-autocleanups.h index 50f469f2..9b15ee47 100644 --- a/glnx-backport-autocleanups.h +++ b/glnx-backport-autocleanups.h @@ -1,5 +1,6 @@ /* * Copyright © 2015 Canonical Limited + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-backport-autoptr.h b/glnx-backport-autoptr.h index b36919dc..8eb91b94 100644 --- a/glnx-backport-autoptr.h +++ b/glnx-backport-autoptr.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald diff --git a/glnx-backports.c b/glnx-backports.c index c016d37e..f9aa7ce5 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/glnx-backports.h b/glnx-backports.h index 6c39cf22..0c5fabe9 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald diff --git a/glnx-console.c b/glnx-console.c index adc8cd88..3ce24527 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2013,2014,2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/glnx-console.h b/glnx-console.h index d853a80c..3d94895b 100644 --- a/glnx-console.h +++ b/glnx-console.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2013,2014,2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published diff --git a/glnx-dirfd.c b/glnx-dirfd.c index 041e1cfe..b039c419 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-dirfd.h b/glnx-dirfd.h index d5773e72..1960820c 100644 --- a/glnx-dirfd.h +++ b/glnx-dirfd.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-errors.c b/glnx-errors.c index 52a61619..cb0df13f 100644 --- a/glnx-errors.c +++ b/glnx-errors.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-errors.h b/glnx-errors.h index cbe74a60..e1dc3a56 100644 --- a/glnx-errors.h +++ b/glnx-errors.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-fdio.c b/glnx-fdio.c index b7ddffdb..b3a62394 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * Portions derived from systemd: * Copyright 2010 Lennart Poettering diff --git a/glnx-fdio.h b/glnx-fdio.h index 3d1f024c..b6ba0ea4 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-local-alloc.c b/glnx-local-alloc.c index 58776b94..8bd55141 100644 --- a/glnx-local-alloc.c +++ b/glnx-local-alloc.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2012,2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index 3be1fa43..615b9548 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2012,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-lockfile.c b/glnx-lockfile.c index 4f7e8c78..65a15585 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -7,6 +7,7 @@ Copyright 2010 Lennart Poettering Copyright 2015 Colin Walters + SPDX-License-Identifier: LGPL-2.1-or-later systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/glnx-lockfile.h b/glnx-lockfile.h index 0adfeac1..6604d707 100644 --- a/glnx-lockfile.h +++ b/glnx-lockfile.h @@ -7,6 +7,7 @@ Copyright 2011 Lennart Poettering Copyright 2015 Colin Walters + SPDX-License-Identifier: LGPL-2.1-or-later systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/glnx-macros.h b/glnx-macros.h index 3177bb66..b92e9e2e 100644 --- a/glnx-macros.h +++ b/glnx-macros.h @@ -3,6 +3,7 @@ * Copyright (C) 2017 Colin Walters * With original source from systemd: * Copyright 2010 Lennart Poettering + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index 0b617bda..3ee9dd56 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -3,6 +3,7 @@ Copyright 2010 Lennart Poettering Copyright 2016 Zbigniew Jędrzejewski-Szmek + SPDX-License-Identifier: LGPL-2.1-or-later systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/glnx-missing.h b/glnx-missing.h index 9d35c40b..5439ae58 100644 --- a/glnx-missing.h +++ b/glnx-missing.h @@ -4,6 +4,7 @@ This file was originally part of systemd. Copyright 2010 Lennart Poettering + SPDX-License-Identifier: LGPL-2.1-or-later systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/glnx-shutil.c b/glnx-shutil.c index 8aa4f21c..5ebe7f88 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-shutil.h b/glnx-shutil.h index 56a99fa2..6a00312e 100644 --- a/glnx-shutil.h +++ b/glnx-shutil.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 1e89bf09..32479cca 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/glnx-xattrs.h b/glnx-xattrs.h index a566a224..b0288504 100644 --- a/glnx-xattrs.h +++ b/glnx-xattrs.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2014,2015 Colin Walters . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/libglnx.doap b/libglnx.doap index 2b38074e..09e58369 100644 --- a/libglnx.doap +++ b/libglnx.doap @@ -1,4 +1,8 @@ + . + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/libglnx.m4 b/libglnx.m4 index 5a72e98b..026a95bd 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -1,3 +1,7 @@ +# Copyright 2016 Colin Walters +# Copyright 2020 Endless Mobile, Inc. +# SPDX-License-Identifier: LGPL-2.1-or-later + AC_DEFUN([LIBGLNX_CONFIGURE], [ AC_CHECK_DECLS([ diff --git a/meson.build b/meson.build index c42cfed8..c9c24d11 100644 --- a/meson.build +++ b/meson.build @@ -1,3 +1,7 @@ +# Copyright 2019 Endless Mobile, Inc. +# Copyright 2019 Collabora Ltd. +# SPDX-License-Identifier: LGPL-2.1-or-later + project( 'libglnx', 'c', diff --git a/tests/libglnx-testlib.c b/tests/libglnx-testlib.c index 879b8766..37b3ecee 100644 --- a/tests/libglnx-testlib.c +++ b/tests/libglnx-testlib.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright 2019 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/libglnx-testlib.h b/tests/libglnx-testlib.h index d45ba860..dccc7e55 100644 --- a/tests/libglnx-testlib.h +++ b/tests/libglnx-testlib.h @@ -2,6 +2,7 @@ * * Copyright (C) 2017 Red Hat, Inc. * Copyright 2019 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/meson.build b/tests/meson.build index de141c5a..ddcae992 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,3 +1,6 @@ +# Copyright 2019 Endless Mobile, Inc. +# Copyright 2019 Collabora Ltd. +# SPDX-License-Identifier: LGPL-2.1-or-later test_names = [ 'errors', diff --git a/tests/test-libglnx-errors.c b/tests/test-libglnx-errors.c index 9e8c1e3b..3b2a4856 100644 --- a/tests/test-libglnx-errors.c +++ b/tests/test-libglnx-errors.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2017 Red Hat, Inc. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 26c0fbc0..01ab1fc5 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2017 Red Hat, Inc. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index 68dc5db2..5f41e5b6 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2017 Red Hat, Inc. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index b4ee838c..a045e765 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright © 2017 Endless Mobile, Inc. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index 0789a2ec..b72b0d06 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2017 Red Hat, Inc. + * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public From bc5d289885e895ac3aeb3158548dc93855683649 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 2 Jul 2021 13:32:45 +0100 Subject: [PATCH 235/280] CI: Run `reuse lint` across the tree Signed-off-by: Simon McVittie --- .gitlab-ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9fa25bc5..38212e0a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,3 +23,12 @@ build: name: "libglnx-${CI_COMMIT_REF_NAME}-${CI_JOB_NAME}" paths: - "${CI_PROJECT_DIR}/_build/meson-logs" + +reuse: + stage: build + image: + name: fsfe/reuse:latest + entrypoint: [""] + before_script: [] + script: + - reuse lint From 68191d8274e18a6e24cfe9db1db507ea8df2a785 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 2 Jul 2021 13:53:07 +0100 Subject: [PATCH 236/280] Update copyright holder for Endless' contributions Will Thompson reports that all of Endless' IP was transferred to Endless OS Foundation LLC. Signed-off-by: Simon McVittie --- .gitlab-ci.yml | 2 +- README.md | 2 +- libglnx.m4 | 2 +- meson.build | 2 +- tests/meson.build | 2 +- tests/test-libglnx-shutil.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38212e0a..29de882e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# Copyright 2019 Endless Mobile, Inc. +# Copyright 2019 Endless OS Foundation LLC # SPDX-License-Identifier: LGPL-2.0-or-later image: registry.fedoraproject.org/fedora:30 diff --git a/README.md b/README.md index f1e0b5c4..ef2cc9d1 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,6 @@ Development happens in GNOME Gitlab: https://gitlab.gnome.org/GNOME/libglnx diff --git a/libglnx.m4 b/libglnx.m4 index 026a95bd..bd0d580c 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -1,5 +1,5 @@ # Copyright 2016 Colin Walters -# Copyright 2020 Endless Mobile, Inc. +# Copyright 2020 Endless OS Foundation LLC # SPDX-License-Identifier: LGPL-2.1-or-later AC_DEFUN([LIBGLNX_CONFIGURE], diff --git a/meson.build b/meson.build index c9c24d11..6714dc24 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -# Copyright 2019 Endless Mobile, Inc. +# Copyright 2019 Endless OS Foundation LLC # Copyright 2019 Collabora Ltd. # SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/tests/meson.build b/tests/meson.build index ddcae992..2ad8c440 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,4 +1,4 @@ -# Copyright 2019 Endless Mobile, Inc. +# Copyright 2019 Endless OS Foundation LLC # Copyright 2019 Collabora Ltd. # SPDX-License-Identifier: LGPL-2.1-or-later diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index a045e765..28b34abe 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -1,6 +1,6 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * - * Copyright © 2017 Endless Mobile, Inc. + * Copyright © 2017 Endless OS Foundation LLC * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or From b6ee0e242347617a461f6ab60ce03d25df503c95 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 22 Feb 2022 10:26:39 +0000 Subject: [PATCH 237/280] tests: Don't copy extended attributes when testing pseudo-files It seems that on SELinux systems, files in /proc have extended attributes that cannot be copied by an unprivileged user. Signed-off-by: Simon McVittie --- tests/test-libglnx-fdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 01ab1fc5..b9aa682a 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -268,7 +268,9 @@ test_filecopy_procfs (void) if (!glnx_file_copy_at (AT_FDCWD, pseudo_files[i], NULL, AT_FDCWD, "copy", - GLNX_FILE_COPY_OVERWRITE | GLNX_FILE_COPY_NOCHOWN, + (GLNX_FILE_COPY_OVERWRITE | + GLNX_FILE_COPY_NOCHOWN | + GLNX_FILE_COPY_NOXATTRS), NULL, error)) return; From b73a467a97688ba4fde002fb69be159c160e4234 Mon Sep 17 00:00:00 2001 From: Phaedrus Leeds Date: Tue, 1 Mar 2022 11:51:44 -0800 Subject: [PATCH 238/280] Add libglnx-config.h to CLEANFILES This fixes make distcheck for Flatpak (but you have to re-run autogen.sh). --- Makefile-libglnx.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index 9d1faa99..bea7b13f 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -25,6 +25,7 @@ EXTRA_DIST += \ $(NULL) BUILT_SOURCES += $(top_builddir)/libglnx-config.h +CLEANFILES += $(top_builddir)/libglnx-config.h $(top_builddir)/libglnx-config.h: Makefile.am echo '#include "config.h"' > $@ From b76cf458cea23d8359237248bf97fde693ce9fe2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 19:43:04 +0100 Subject: [PATCH 239/280] meson: Automatically link libglnx-dependent objects to gio-unix If we don't do this, users of libglnx all have to add this dependency themselves, otherwise they'll get errors like: fatal error: gio/gfiledescriptorbased.h: No such file or directory Signed-off-by: Simon McVittie --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 6714dc24..1fc438e5 100644 --- a/meson.build +++ b/meson.build @@ -86,6 +86,7 @@ libglnx = static_library('glnx', include_directories : libglnx_inc, install : false) libglnx_dep = declare_dependency( + dependencies : libglnx_deps, include_directories : libglnx_inc, link_with : libglnx) From eeea247ade9efc797d7aad88baa9e93742d9a9bf Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 18:36:14 +0100 Subject: [PATCH 240/280] meson: Always build with hidden symbol visibility This copylib isn't intended to be part of anyone's ABI. In Autotools, this is typically implemented by doing some ad-hoc compiler checks and adding -fvisibility=hidden to the global CFLAGS of any library that pulls in libglnx, but in Meson it's a build system feature. Signed-off-by: Simon McVittie --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 1fc438e5..f3dc8a40 100644 --- a/meson.build +++ b/meson.build @@ -83,6 +83,7 @@ libglnx_sources = [ libglnx = static_library('glnx', libglnx_sources, dependencies : libglnx_deps, + gnu_symbol_visibility : 'hidden', include_directories : libglnx_inc, install : false) libglnx_dep = declare_dependency( From c500c362b85ad8119a6f07d91e47166c997753c9 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 18:33:18 +0100 Subject: [PATCH 241/280] tests: Build libglnx-testlib as a separate static library One of Flatpak's unit tests uses this, for _GLNX_TEST_SCOPED_TEMP_DIR. Its API is not really stable, but libglnx is a "copylib" anyway, so none of it has (or needs) a strictly stable API. Signed-off-by: Simon McVittie --- tests/meson.build | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 2ad8c440..555009f2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -2,6 +2,25 @@ # Copyright 2019 Collabora Ltd. # SPDX-License-Identifier: LGPL-2.1-or-later +libglnx_testlib = static_library( + 'glnx-testlib', + 'libglnx-testlib.c', + 'libglnx-testlib.h', + dependencies : [ + libglnx_dep, + libglnx_deps, + ], + install : false, +) +libglnx_testlib_dep = declare_dependency( + dependencies : [ + libglnx_dep, + libglnx_deps, + ], + include_directories : include_directories('.'), + link_with : libglnx_testlib, +) + test_names = [ 'errors', 'fdio', @@ -13,13 +32,12 @@ test_names = [ foreach test_name : test_names exe = executable(test_name, [ - 'libglnx-testlib.c', - 'libglnx-testlib.h', 'test-libglnx-' + test_name + '.c', ], dependencies: [ libglnx_dep, libglnx_deps, + libglnx_testlib_dep, ], ) test(test_name, exe) From 96ba9f4d8d747889a10e1a84594922e25fde495c Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 18:51:09 +0100 Subject: [PATCH 242/280] tests: Optionally skip building and running the actual tests If we're building libglnx as a subproject in a larger project, we won't necessarily want to run these. Signed-off-by: Simon McVittie --- meson_options.txt | 9 +++++++++ tests/meson.build | 42 ++++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 meson_options.txt diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..1028017f --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,9 @@ +# Copyright 2022 Collabora Ltd. +# SPDX-License-Identifier: LGPL-2.0-or-later + +option( + 'tests', + type : 'boolean', + description : 'build and run unit tests', + value : 'true', +) diff --git a/tests/meson.build b/tests/meson.build index 555009f2..2c38ab09 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -21,24 +21,26 @@ libglnx_testlib_dep = declare_dependency( link_with : libglnx_testlib, ) -test_names = [ - 'errors', - 'fdio', - 'macros', - 'shutil', - 'xattrs', -] +if get_option('tests') + test_names = [ + 'errors', + 'fdio', + 'macros', + 'shutil', + 'xattrs', + ] -foreach test_name : test_names - exe = executable(test_name, - [ - 'test-libglnx-' + test_name + '.c', - ], - dependencies: [ - libglnx_dep, - libglnx_deps, - libglnx_testlib_dep, - ], - ) - test(test_name, exe) -endforeach + foreach test_name : test_names + exe = executable(test_name, + [ + 'test-libglnx-' + test_name + '.c', + ], + dependencies: [ + libglnx_dep, + libglnx_deps, + libglnx_testlib_dep, + ], + ) + test(test_name, exe) + endforeach +endif From 4d5b1fcf0257f508f4b5e48b099f259b7bb9c908 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 18:33:34 +0100 Subject: [PATCH 243/280] tests: Verify libglnx can work as a subproject What isn't tested usually doesn't work, and the only way to use libglnx is as a subproject, so test it like that. Signed-off-by: Simon McVittie --- .gitlab-ci.yml | 17 ++++++++++----- tests/use-as-subproject/.gitignore | 5 +++++ tests/use-as-subproject/README | 8 ++++++++ tests/use-as-subproject/config.h | 6 ++++++ tests/use-as-subproject/dummy-config.h.in | 6 ++++++ tests/use-as-subproject/meson.build | 25 +++++++++++++++++++++++ tests/use-as-subproject/use-libglnx.c | 16 +++++++++++++++ tests/use-as-subproject/use-testlib.c | 17 +++++++++++++++ 8 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 tests/use-as-subproject/.gitignore create mode 100644 tests/use-as-subproject/README create mode 100644 tests/use-as-subproject/config.h create mode 100644 tests/use-as-subproject/dummy-config.h.in create mode 100644 tests/use-as-subproject/meson.build create mode 100644 tests/use-as-subproject/use-libglnx.c create mode 100644 tests/use-as-subproject/use-testlib.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29de882e..d9e465aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,17 +7,24 @@ stages: - build before_script: - - dnf install -y gcc meson ninja-build "pkgconfig(gio-2.0)" "pkgconfig(gio-unix-2.0)" "pkgconfig(glib-2.0)" + - dnf install -y gcc git meson ninja-build "pkgconfig(gio-2.0)" "pkgconfig(gio-unix-2.0)" "pkgconfig(glib-2.0)" xz build: stage: build script: - meson _build . - - cd _build - - ninja - - meson test + - ninja -C _build + - meson test -C _build # Run it again! This previously did not work. - - meson test + - meson test -C _build + # Ensure that we can build as a subproject + - rm -fr _build/meson-dist + - meson dist -C _build + - mkdir -p tests/use-as-subproject/subprojects/libglnx + - tar --strip-components=1 -C tests/use-as-subproject/subprojects/libglnx -xf _build/meson-dist/*.tar.xz + - meson tests/use-as-subproject/_build tests/use-as-subproject + - ninja -C tests/use-as-subproject/_build + - meson test -C tests/use-as-subproject/_build artifacts: when: on_failure name: "libglnx-${CI_COMMIT_REF_NAME}-${CI_JOB_NAME}" diff --git a/tests/use-as-subproject/.gitignore b/tests/use-as-subproject/.gitignore new file mode 100644 index 00000000..ec6149fa --- /dev/null +++ b/tests/use-as-subproject/.gitignore @@ -0,0 +1,5 @@ +# Copyright 2022 Collabora Ltd. +# SPDX-License-Identifier: LGPL-2.0-or-later + +/_build/ +/subprojects/ diff --git a/tests/use-as-subproject/README b/tests/use-as-subproject/README new file mode 100644 index 00000000..cc43a093 --- /dev/null +++ b/tests/use-as-subproject/README @@ -0,0 +1,8 @@ +This is a simple example of a project that uses libglnx as a subproject. +The intention is that if this project can successfully build and use libglnx +as a subproject, then so could Flatpak. + + diff --git a/tests/use-as-subproject/config.h b/tests/use-as-subproject/config.h new file mode 100644 index 00000000..dffc647b --- /dev/null +++ b/tests/use-as-subproject/config.h @@ -0,0 +1,6 @@ +/* + * Copyright 2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#error Should not use superproject config.h to compile libglnx diff --git a/tests/use-as-subproject/dummy-config.h.in b/tests/use-as-subproject/dummy-config.h.in new file mode 100644 index 00000000..bffb52ac --- /dev/null +++ b/tests/use-as-subproject/dummy-config.h.in @@ -0,0 +1,6 @@ +/* + * Copyright 2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#error Should not use superproject generated config.h to compile libglnx diff --git a/tests/use-as-subproject/meson.build b/tests/use-as-subproject/meson.build new file mode 100644 index 00000000..65ba37bb --- /dev/null +++ b/tests/use-as-subproject/meson.build @@ -0,0 +1,25 @@ +# Copyright 2022 Collabora Ltd. +# SPDX-License-Identifier: LGPL-2.0-or-later + +project( + 'use-libglnx-as-subproject', + 'c', + version : '0', + meson_version : '>=0.49.0', +) +add_project_arguments('-D_GNU_SOURCE', language : 'c') + +configure_file( + copy : true, + input : 'dummy-config.h.in', + output : 'config.h', +) + +glib_dep = dependency('glib-2.0') + +libglnx = subproject('libglnx') +libglnx_dep = libglnx.get_variable('libglnx_dep') +libglnx_testlib_dep = libglnx.get_variable('libglnx_testlib_dep') + +executable('use-libglnx', 'use-libglnx.c', dependencies : [libglnx_dep, glib_dep]) +executable('use-testlib', 'use-testlib.c', dependencies : [libglnx_testlib_dep, glib_dep]) diff --git a/tests/use-as-subproject/use-libglnx.c b/tests/use-as-subproject/use-libglnx.c new file mode 100644 index 00000000..0e14db0b --- /dev/null +++ b/tests/use-as-subproject/use-libglnx.c @@ -0,0 +1,16 @@ +/* + * Copyright 2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include + +int +main (void) +{ + GError *error = NULL; + + glnx_throw (&error, "whatever"); + g_clear_error (&error); + return 0; +} diff --git a/tests/use-as-subproject/use-testlib.c b/tests/use-as-subproject/use-testlib.c new file mode 100644 index 00000000..9a955b47 --- /dev/null +++ b/tests/use-as-subproject/use-testlib.c @@ -0,0 +1,17 @@ +/* + * Copyright 2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include +#include + +int +main (void) +{ + _GLNX_TEST_DECLARE_ERROR (local_error, error); + + glnx_throw (error, "Whatever"); + g_clear_error (&local_error); + return 0; +} From 238f7bfc494e16a0864f88ae891da6e7b3d82e5b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 21 Apr 2022 19:56:41 +0100 Subject: [PATCH 244/280] fdio: Skip glnx_try_fallocate() if _GNU_SOURCE is not defined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fallocate() is only visible in fcntl.h if _GNU_SOURCE is defined. Most users of libglnx will want to do that anyway, but it seems nicer to avoid "implicit declaration of function ‘fallocate’" warnings from simply including into naive code. Signed-off-by: Simon McVittie --- glnx-fdio.h | 2 ++ tests/use-as-subproject/meson.build | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/glnx-fdio.h b/glnx-fdio.h index b6ba0ea4..af534795 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -209,6 +209,7 @@ int glnx_renameat2_noreplace (int olddirfd, const char *oldpath, int glnx_renameat2_exchange (int olddirfd, const char *oldpath, int newdirfd, const char *newpath); +#ifdef _GNU_SOURCE /** * glnx_try_fallocate: * @fd: File descriptor @@ -240,6 +241,7 @@ glnx_try_fallocate (int fd, return TRUE; } +#endif /** * glnx_fstat: diff --git a/tests/use-as-subproject/meson.build b/tests/use-as-subproject/meson.build index 65ba37bb..2d081604 100644 --- a/tests/use-as-subproject/meson.build +++ b/tests/use-as-subproject/meson.build @@ -7,7 +7,6 @@ project( version : '0', meson_version : '>=0.49.0', ) -add_project_arguments('-D_GNU_SOURCE', language : 'c') configure_file( copy : true, From f8b7343a29ffae3906d616dc4f6fa4185341e159 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:02:09 +0100 Subject: [PATCH 245/280] console: Mark an unused parameter as such This signal handler is only called for SIGWINCH, so it doesn't need to look at its parameter. Signed-off-by: Simon McVittie --- glnx-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-console.c b/glnx-console.c index 3ce24527..f6ce0d49 100644 --- a/glnx-console.c +++ b/glnx-console.c @@ -143,7 +143,7 @@ glnx_console_lines (void) } static void -on_sigwinch (int signum) +on_sigwinch (G_GNUC_UNUSED int signum) { cached_columns = 0; cached_lines = 0; From 2d007fa1c0dcbd93206139ed10e238f3696c5206 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:02:48 +0100 Subject: [PATCH 246/280] fdio, xattrs: Mark unused cancellables as such These functions take a GCancellable for consistency with other file APIs, but are not actually cancellable at the moment. Signed-off-by: Simon McVittie --- glnx-fdio.c | 4 ++-- glnx-xattrs.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index b3a62394..c2896f3f 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -660,7 +660,7 @@ glnx_file_get_contents_utf8_at (int dfd, char * glnx_readlinkat_malloc (int dfd, const char *subpath, - GCancellable *cancellable, + G_GNUC_UNUSED GCancellable *cancellable, GError **error) { dfd = glnx_dirfd_canonicalize (dfd); @@ -1106,7 +1106,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, uid_t uid, gid_t gid, GLnxFileReplaceFlags flags, - GCancellable *cancellable, + G_GNUC_UNUSED GCancellable *cancellable, GError **error) { char *dnbuf = strdupa (subpath); diff --git a/glnx-xattrs.c b/glnx-xattrs.c index 32479cca..d0e6ab3d 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -141,7 +141,7 @@ static gboolean get_xattrs_impl (const char *path, int fd, GVariant **out_xattrs, - GCancellable *cancellable, + G_GNUC_UNUSED GCancellable *cancellable, GError **error) { gboolean ret = FALSE; @@ -269,7 +269,7 @@ glnx_dfd_name_get_all_xattrs (int dfd, static gboolean set_all_xattrs_for_path (const char *path, GVariant *xattrs, - GCancellable *cancellable, + G_GNUC_UNUSED GCancellable *cancellable, GError **error) { const guint n = g_variant_n_children (xattrs); @@ -337,7 +337,7 @@ glnx_dfd_name_set_all_xattrs (int dfd, gboolean glnx_fd_set_all_xattrs (int fd, GVariant *xattrs, - GCancellable *cancellable, + G_GNUC_UNUSED GCancellable *cancellable, GError **error) { const guint n = g_variant_n_children (xattrs); From d78121a6997c833cf3c367b432f668551ee3ae1e Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:04:00 +0100 Subject: [PATCH 247/280] fdio: Make signedness of iterator consistent with limit gcc -Wsign-compare warns about this. The iteration limit is constant and small enough that either int or guint would be fine. Signed-off-by: Simon McVittie --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index c2896f3f..c601f08e 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -229,7 +229,7 @@ open_tmpfile_core (int dfd, const char *subpath, const guint count_max = 100; { g_autofree char *tmp = g_strconcat (subpath, "/tmp.XXXXXX", NULL); - for (int count = 0; count < count_max; count++) + for (guint count = 0; count < count_max; count++) { glnx_gen_temp_name (tmp); From f65b05f2d540e8560fde374c206f872e24436e32 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:04:58 +0100 Subject: [PATCH 248/280] fdio: Match signedness of length parameter This is documented as taking a length of -1, but the parameter is unsigned, so it's more correctly ((gsize) -1). This silences a warning from gcc -Wsign-compare. Signed-off-by: Simon McVittie --- glnx-fdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index c601f08e..ee69aa12 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -1130,7 +1130,7 @@ glnx_file_replace_contents_with_perms_at (int dfd, &tmpf, error)) return FALSE; - if (len == -1) + if (len == (gsize) -1) len = strlen ((char*)buf); if (!glnx_try_fallocate (tmpf.fd, 0, len, error)) From 4c51a54c36eb1a9d728acb14a700271adc7e0ee4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:06:51 +0100 Subject: [PATCH 249/280] tests: Make iterator signedness agree with limit g_variant_n_children() returns a size_t (or gsize, which is equivalent) so in principle it could overflow an int, although in practice we are not going to have that many extended attributes. This silences a warning from gcc -Wsign-compare. Signed-off-by: Simon McVittie --- tests/test-libglnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index b72b0d06..ad52b760 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -122,7 +122,7 @@ do_write_run (GLnxDirFdIterator *dfd_iter, GError **error) if (!glnx_fd_get_all_xattrs (fd, ¤t_xattrs, NULL, error)) return FALSE; - for (int i = 0; i < g_variant_n_children (current_xattrs); i++) + for (size_t i = 0; i < g_variant_n_children (current_xattrs); i++) { const char *name, *value; g_variant_get_child (current_xattrs, i, "(^&ay^&ay)", &name, &value); From 7b26e39a1f687b1a209b73fecd76c083037d5c2f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:07:38 +0100 Subject: [PATCH 250/280] tests: Make signedness of timestamp agree This silences a warning from gcc -Wsign-compare. g_get_monotonic_time() returns a signed int64. Signed-off-by: Simon McVittie --- tests/test-libglnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-libglnx-xattrs.c b/tests/test-libglnx-xattrs.c index ad52b760..1de9acdb 100644 --- a/tests/test-libglnx-xattrs.c +++ b/tests/test-libglnx-xattrs.c @@ -182,7 +182,7 @@ xattr_thread (gpointer data) g_autoptr(GError) local_error = NULL; GError **error = &local_error; struct XattrWorker *worker = data; - guint64 end_time = g_get_monotonic_time () + XATTR_THREAD_RUN_TIME_USECS; + gint64 end_time = g_get_monotonic_time () + XATTR_THREAD_RUN_TIME_USECS; guint n_read = 0; while (g_get_monotonic_time () < end_time) From 9f033ee2f6fb2b7ef238b4d6609d55a1e1e8e3e2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sun, 24 Apr 2022 11:08:01 +0100 Subject: [PATCH 251/280] meson: Build with more warnings by default If we keep this module closer to warnings-clean, then it can be submoduled into Meson projects that enable warnings by default without having to override it to a lower warning-level. Signed-off-by: Simon McVittie --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 6714dc24..08622f2d 100644 --- a/meson.build +++ b/meson.build @@ -7,6 +7,7 @@ project( 'c', default_options : [ 'c_std=gnu99', + 'warning_level=3', ], ) From af02572e6bd5d36fd5c45252723ec8916e6c220f Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 13:57:19 +0100 Subject: [PATCH 252/280] build: Reduce default warning level from 3 to 2 Warning level 2 is -Wall -Wextra, and warning level 3 adds -Wpedantic (warnings about use of non-ISO extensions). libglnx is intentionally using non-ISO features, so -Wpedantic is counterproductive. Signed-off-by: Simon McVittie --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4787c859..38ec2ea7 100644 --- a/meson.build +++ b/meson.build @@ -7,7 +7,7 @@ project( 'c', default_options : [ 'c_std=gnu99', - 'warning_level=3', + 'warning_level=2', ], ) From c5e1b295bc0f60b4562cd3db0379c0822ed758f1 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 16:36:27 +0100 Subject: [PATCH 253/280] backports: Backport g_info() The rest of the g_log() wrappers have been there since time immemorial, but g_info() was initially omitted. It's useful for projects like Flatpak that want to have two levels of off-by-default logging, which map nicely to info and debug. Signed-off-by: Simon McVittie --- glnx-backports.h | 4 ++++ tests/test-libglnx-macros.c | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index 0c5fabe9..bdc4bbe4 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -48,6 +48,10 @@ G_BEGIN_DECLS } G_STMT_END #endif +#if !GLIB_CHECK_VERSION(2, 40, 0) +#define g_info(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__) +#endif + #if !GLIB_CHECK_VERSION(2, 44, 0) #define g_strv_contains glnx_strv_contains diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c index 5f41e5b6..ec669d32 100644 --- a/tests/test-libglnx-macros.c +++ b/tests/test-libglnx-macros.c @@ -26,6 +26,13 @@ #include #include +static void +test_info (void) +{ + g_info ("hello, world"); + g_info ("answer=%d", 42); +} + static void test_inset (void) { @@ -104,6 +111,7 @@ test_hash_table_foreach (void) int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); + g_test_add_func ("/info", test_info); g_test_add_func ("/inset", test_inset); g_test_add_func ("/hash_table_foreach", test_hash_table_foreach); return g_test_run(); From 7a2f26a312c92e335652962bb99bf2e160720c8d Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 29 Jul 2022 17:29:28 -0400 Subject: [PATCH 254/280] fdio: fix fd offset handling with `FICLONE` When using `FICLONE`, the kernel does a full file clone and disregards the fd offsets. Users of this API however assume that it *is* offset-sensitive. So we need to verify that the fd offsets are at the start of the files before we call `FICLONE`. This was done in systemd also in: https://github.com/systemd/systemd/commit/c622fbdb8d37 The commit message does not explain this but `ioctl_ficlone(2)` says: The `FICLONE` ioctl clones entire files. (Compare with `FICLONERANGE`, which takes a struct with offsets and the length). Similarly, we need to seek to the end of the file descriptors on success so that we're consistent with the behaviour of the other backends available (`copy_file_range`, `sendfile` and manual copying). This also matches what systemd does nowadays: https://github.com/systemd/systemd/blob/80f967311ac5/src/shared/copy.c#L199 --- glnx-fdio.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index ee69aa12..b1af6793 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -794,10 +794,21 @@ glnx_regfile_copy_bytes (int fdf, int fdt, off_t max_bytes) /* If we've requested to copy the whole range, try a full-file clone first. */ - if (max_bytes == (off_t) -1) + if (max_bytes == (off_t) -1 && + lseek (fdf, 0, SEEK_CUR) == 0 && + lseek (fdt, 0, SEEK_CUR) == 0) { if (ioctl (fdt, FICLONE, fdf) == 0) - return 0; + { + /* All the other methods advance the fds. Do it here too for consistency. */ + if (lseek (fdf, 0, SEEK_END) < 0) + return -1; + if (lseek (fdt, 0, SEEK_END) < 0) + return -1; + + return 0; + } + /* Fall through */ struct stat stbuf; From fabcdb3bd8c07fff03bc325a140df358c307855b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 15:42:43 +0100 Subject: [PATCH 255/280] backports: Backport G_SPAWN_DEFAULT This makes the absence of flags a bit clearer to write. Signed-off-by: Simon McVittie --- glnx-backports.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index 0c5fabe9..3de9370f 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -64,6 +64,10 @@ gboolean glnx_set_object (GObject **object_ptr, #endif /* !GLIB_CHECK_VERSION(2, 44, 0) */ +#if !GLIB_CHECK_VERSION(2, 38, 0) +#define G_SPAWN_DEFAULT ((GSpawnFlags) 0) +#endif + #ifndef g_assert_nonnull #define g_assert_nonnull(x) g_assert (x != NULL) #endif From b5658723ffafa74f5feee7f3939ec652e9020c8a Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 15:43:15 +0100 Subject: [PATCH 256/280] backports: Backport G_OPTION_FLAG_NONE, G_OPTION_ENTRY_NULL These make tables of options a bit clearer to write. Signed-off-by: Simon McVittie --- glnx-backports.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index 3de9370f..fd769efc 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -68,6 +68,14 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_SPAWN_DEFAULT ((GSpawnFlags) 0) #endif +#if !GLIB_CHECK_VERSION(2, 42, 0) +#define G_OPTION_FLAG_NONE ((GOptionFlags) 0) +#endif + +#if !GLIB_CHECK_VERSION(2, 70, 0) +#define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } +#endif + #ifndef g_assert_nonnull #define g_assert_nonnull(x) g_assert (x != NULL) #endif From 29681b4b77f2706b10b7f8c1ab47f695dce21e21 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 16:19:36 +0100 Subject: [PATCH 257/280] backports: Backport G_DBUS_METHOD_INVOCATION_HANDLED, _UNHANDLED These are convenient for the same reasons as G_SOURCE_REMOVE and G_SOURCE_CONTINUE. Signed-off-by: Simon McVittie --- glnx-backports.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index fd769efc..6f766a37 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -72,6 +72,14 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_OPTION_FLAG_NONE ((GOptionFlags) 0) #endif +#ifndef G_DBUS_METHOD_INVOCATION_HANDLED /* added in 2.68 */ +#define G_DBUS_METHOD_INVOCATION_HANDLED TRUE +#endif + +#ifndef G_DBUS_METHOD_INVOCATION_UNHANDLED /* added in 2.68 */ +#define G_DBUS_METHOD_INVOCATION_UNHANDLED FALSE +#endif + #if !GLIB_CHECK_VERSION(2, 70, 0) #define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } #endif From 9a0e6fd00473ad2c529ec86249b48d88e893d165 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 15:24:37 +0100 Subject: [PATCH 258/280] testlib: Be compatible with ancient GLib by using glnx_close_fd This is an enabler for testing a backport of GTest helpers (g_test_skip(), etc.) in the oldest environment that I have conveniently available (Steam Runtime 1 'scout' with GLib 2.32). Signed-off-by: Simon McVittie --- tests/libglnx-testlib.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/libglnx-testlib.c b/tests/libglnx-testlib.c index 37b3ecee..3eb2ba14 100644 --- a/tests/libglnx-testlib.c +++ b/tests/libglnx-testlib.c @@ -67,8 +67,7 @@ _glnx_test_auto_temp_dir_leave (_GLnxTestAutoTempDir *dir) glnx_tmpdir_delete (&dir->temp_dir, NULL, &error); g_assert_no_error (error); - g_close (dir->old_cwd_fd, &error); - g_assert_no_error (error); + glnx_close_fd (&dir->old_cwd_fd); g_free (dir->old_cwd); g_free (dir); From 5a60c0bca5d91ba701ca4a2f9bcfae9eec65253b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Sat, 2 Jul 2022 17:33:56 +0100 Subject: [PATCH 259/280] Move assertion/test utilities to a separate header A lot of recent convenience APIs can easily be backported, but they'll clutter glnx-backports.h. In preparation, split these out. Another reason to separate these is that when we start adding backports of test utilities that need implementation code in the libglnx static library, we'll want their implementations to be in a separate translation unit, so that they don't get linked into production executables, only into tests. Signed-off-by: Simon McVittie --- Makefile-libglnx.am | 1 + glnx-backport-testutils.h | 43 +++++++++++++++++++++++++++++++++++++++ glnx-backports.h | 12 ----------- libglnx.h | 1 + meson.build | 1 + 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 glnx-backport-testutils.h diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index bea7b13f..f699ff2d 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -33,6 +33,7 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-macros.h \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ + $(libglnx_srcpath)/glnx-backport-testutils.h \ $(libglnx_srcpath)/glnx-backports.h \ $(libglnx_srcpath)/glnx-backports.c \ $(libglnx_srcpath)/glnx-local-alloc.h \ diff --git a/glnx-backport-testutils.h b/glnx-backport-testutils.h new file mode 100644 index 00000000..f43c204b --- /dev/null +++ b/glnx-backport-testutils.h @@ -0,0 +1,43 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright 2015 Colin Walters + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +#include "glnx-backports.h" + +G_BEGIN_DECLS + +#ifndef g_assert_nonnull +#define g_assert_nonnull(x) g_assert (x != NULL) +#endif + +#ifndef g_assert_null +#define g_assert_null(x) g_assert (x == NULL) +#endif + +#if !GLIB_CHECK_VERSION (2, 38, 0) +#define g_test_skip(s) g_test_message ("SKIP: %s", s) +#endif + +G_END_DECLS diff --git a/glnx-backports.h b/glnx-backports.h index 6f766a37..b535e2df 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -84,16 +84,4 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } #endif -#ifndef g_assert_nonnull -#define g_assert_nonnull(x) g_assert (x != NULL) -#endif - -#ifndef g_assert_null -#define g_assert_null(x) g_assert (x == NULL) -#endif - -#if !GLIB_CHECK_VERSION (2, 38, 0) -#define g_test_skip(s) g_test_message ("SKIP: %s", s) -#endif - G_END_DECLS diff --git a/libglnx.h b/libglnx.h index ca82ba5c..63d73adc 100644 --- a/libglnx.h +++ b/libglnx.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS #include #include #include +#include #include #include #include diff --git a/meson.build b/meson.build index 4787c859..86535a8a 100644 --- a/meson.build +++ b/meson.build @@ -57,6 +57,7 @@ libglnx_inc = include_directories('.') libglnx_sources = [ 'glnx-backport-autocleanups.h', 'glnx-backport-autoptr.h', + 'glnx-backport-testutils.h', 'glnx-backports.c', 'glnx-backports.h', 'glnx-console.c', From c52b73d4ae58ee2e3f1572c1b546d929ecdbd0c8 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 27 Jul 2022 15:51:41 +0100 Subject: [PATCH 260/280] Backport most of the test convenience helpers from GLib's GTest This includes a test (who tests the tests themselves?). Run as `${build}/tests/testing --tap` with semi-modern GLib, or `${build}/tests/testing --verbose` with GLib < 2.38, to check that the output is as reasonable as possible given the limitations of the GLib version in use. Signed-off-by: Simon McVittie --- Makefile-libglnx.am | 1 + glnx-backport-testutils.c | 145 ++++++++++++++ glnx-backport-testutils.h | 157 ++++++++++++++- glnx-backports.h | 6 + meson.build | 1 + tests/meson.build | 11 ++ tests/test-libglnx-testing.c | 361 +++++++++++++++++++++++++++++++++++ tests/testing-helper.c | 327 +++++++++++++++++++++++++++++++ 8 files changed, 1007 insertions(+), 2 deletions(-) create mode 100644 glnx-backport-testutils.c create mode 100644 tests/test-libglnx-testing.c create mode 100644 tests/testing-helper.c diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index f699ff2d..5934a2b6 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -34,6 +34,7 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-backport-autocleanups.h \ $(libglnx_srcpath)/glnx-backport-autoptr.h \ $(libglnx_srcpath)/glnx-backport-testutils.h \ + $(libglnx_srcpath)/glnx-backport-testutils.c \ $(libglnx_srcpath)/glnx-backports.h \ $(libglnx_srcpath)/glnx-backports.c \ $(libglnx_srcpath)/glnx-local-alloc.h \ diff --git a/glnx-backport-testutils.c b/glnx-backport-testutils.c new file mode 100644 index 00000000..31105c90 --- /dev/null +++ b/glnx-backport-testutils.c @@ -0,0 +1,145 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright 2015 Colin Walters + * Copyright 2020 Niels De Graef + * Copyright 2021-2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This program 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 licence 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "libglnx-config.h" + +#include +#include + +#include + +#include "glnx-backport-autocleanups.h" +#include "glnx-backport-autoptr.h" +#include "glnx-backport-testutils.h" +#include "glnx-backports.h" + +#if !GLIB_CHECK_VERSION (2, 68, 0) +/* Backport of g_assertion_message_cmpstrv() */ +void +_glnx_assertion_message_cmpstrv (const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + const char * const *arg1, + const char * const *arg2, + gsize first_wrong_idx) +{ + const char *s1 = arg1[first_wrong_idx], *s2 = arg2[first_wrong_idx]; + char *a1, *a2, *s, *t1 = NULL, *t2 = NULL; + + a1 = g_strconcat ("\"", t1 = g_strescape (s1, NULL), "\"", NULL); + a2 = g_strconcat ("\"", t2 = g_strescape (s2, NULL), "\"", NULL); + g_free (t1); + g_free (t2); + s = g_strdup_printf ("assertion failed (%s): first differing element at index %" G_GSIZE_FORMAT ": %s does not equal %s", + expr, first_wrong_idx, a1, a2); + g_free (a1); + g_free (a2); + g_assertion_message (domain, file, line, func, s); + g_free (s); +} +#endif + +#if !GLIB_CHECK_VERSION(2, 70, 0) +/* + * Same as g_test_message(), but split messages with newlines into + * multiple separate messages to avoid corrupting stdout, even in older + * GLib versions that didn't do this + */ +void +_glnx_test_message_safe (const char *format, + ...) +{ + g_autofree char *message = NULL; + va_list ap; + char *line; + char *saveptr = NULL; + + va_start (ap, format); + g_vasprintf (&message, format, ap); + va_end (ap); + + for (line = strtok_r (message, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + (g_test_message) ("%s", line); +} + +/* Backport of g_test_fail_printf() */ +void +_glnx_test_fail_printf (const char *format, + ...) +{ + g_autofree char *message = NULL; + va_list ap; + + va_start (ap, format); + g_vasprintf (&message, format, ap); + va_end (ap); + + /* This is the closest we can do in older GLib */ + g_test_message ("Bail out! %s", message); + g_test_fail (); +} + +/* Backport of g_test_skip_printf() */ +void +_glnx_test_skip_printf (const char *format, + ...) +{ + g_autofree char *message = NULL; + va_list ap; + + va_start (ap, format); + g_vasprintf (&message, format, ap); + va_end (ap); + + g_test_skip (message); +} + +/* Backport of g_test_incomplete_printf() */ +void +_glnx_test_incomplete_printf (const char *format, + ...) +{ + g_autofree char *message = NULL; + va_list ap; + + va_start (ap, format); + g_vasprintf (&message, format, ap); + va_end (ap); + +#if GLIB_CHECK_VERSION(2, 58, 0) + /* Since 2.58, g_test_incomplete() sets the exit status correctly. */ + g_test_incomplete (message); +#elif GLIB_CHECK_VERSION (2, 38, 0) + /* Before 2.58, g_test_incomplete() was treated like a failure for the + * purposes of setting the exit status, so prefer to use (our wrapper + * around) g_test_skip(). */ + g_test_skip_printf ("TODO: %s", message); +#else + g_test_message ("TODO: %s", message); +#endif +} +#endif diff --git a/glnx-backport-testutils.h b/glnx-backport-testutils.h index f43c204b..2febcc0c 100644 --- a/glnx-backport-testutils.h +++ b/glnx-backport-testutils.h @@ -2,6 +2,12 @@ * * Copyright 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * Copyright 2015 Colin Walters + * Copyright 2014 Dan Winship + * Copyright 2015 Colin Walters + * Copyright 2017 Emmanuele Bassi + * Copyright 2018-2019 Endless OS Foundation LLC + * Copyright 2020 Niels De Graef + * Copyright 2021-2022 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or @@ -28,16 +34,163 @@ G_BEGIN_DECLS -#ifndef g_assert_nonnull +#ifndef g_assert_nonnull /* added in 2.40 */ #define g_assert_nonnull(x) g_assert (x != NULL) #endif -#ifndef g_assert_null +#ifndef g_assert_null /* added in 2.40 */ #define g_assert_null(x) g_assert (x == NULL) #endif #if !GLIB_CHECK_VERSION (2, 38, 0) +/* Not exactly equivalent, but close enough */ #define g_test_skip(s) g_test_message ("SKIP: %s", s) #endif +#if !GLIB_CHECK_VERSION (2, 58, 0) +/* Before 2.58, g_test_incomplete() didn't set the exit status correctly */ +#define g_test_incomplete(s) _glnx_test_incomplete_printf ("%s", s) +#endif + +#if !GLIB_CHECK_VERSION (2, 46, 0) +#define g_assert_cmpmem(m1, l1, m2, l2) G_STMT_START {\ + gconstpointer __m1 = m1, __m2 = m2; \ + int __l1 = l1, __l2 = l2; \ + if (__l1 != 0 && __m1 == NULL) \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #l1 " == 0 || " #m1 " != NULL)"); \ + else if (__l2 != 0 && __m2 == NULL) \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #l2 " == 0 || " #m2 " != NULL)"); \ + else if (__l1 != __l2) \ + g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #l1 " (len(" #m1 ")) == " #l2 " (len(" #m2 "))", \ + (long double) __l1, "==", (long double) __l2, 'i'); \ + else if (__l1 != 0 && __m2 != NULL && memcmp (__m1, __m2, __l1) != 0) \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #m1 " == " #m2 ")"); \ + } G_STMT_END +#endif + +#if !GLIB_CHECK_VERSION (2, 58, 0) +#define g_assert_cmpfloat_with_epsilon(n1,n2,epsilon) \ + G_STMT_START { \ + double __n1 = (n1), __n2 = (n2), __epsilon = (epsilon); \ + if (G_APPROX_VALUE (__n1, __n2, __epsilon)) ; else \ + g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #n1 " == " #n2 " (+/- " #epsilon ")", __n1, "==", __n2, 'f'); \ + } G_STMT_END +#endif + +#if !GLIB_CHECK_VERSION (2, 60, 0) +#define g_assert_cmpvariant(v1, v2) \ + G_STMT_START \ + { \ + GVariant *__v1 = (v1), *__v2 = (v2); \ + if (!g_variant_equal (__v1, __v2)) \ + { \ + gchar *__s1, *__s2, *__msg; \ + __s1 = g_variant_print (__v1, TRUE); \ + __s2 = g_variant_print (__v2, TRUE); \ + __msg = g_strdup_printf ("assertion failed (" #v1 " == " #v2 "): %s does not equal %s", __s1, __s2); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ + g_free (__s1); \ + g_free (__s2); \ + g_free (__msg); \ + } \ + } \ + G_STMT_END +#endif + +#if !GLIB_CHECK_VERSION (2, 62, 0) +/* Not exactly equivalent, but close enough */ +#define g_test_summary(s) g_test_message ("SUMMARY: %s", s) +#endif + +#if !GLIB_CHECK_VERSION (2, 66, 0) +#define g_assert_no_errno(expr) G_STMT_START { \ + int __ret, __errsv; \ + errno = 0; \ + __ret = expr; \ + __errsv = errno; \ + if (__ret < 0) \ + { \ + gchar *__msg; \ + __msg = g_strdup_printf ("assertion failed (" #expr " >= 0): errno %i: %s", __errsv, g_strerror (__errsv)); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ + g_free (__msg); \ + } \ + } G_STMT_END +#endif + +#if !GLIB_CHECK_VERSION (2, 68, 0) +#define g_assertion_message_cmpstrv _glnx_assertion_message_cmpstrv +void _glnx_assertion_message_cmpstrv (const char *domain, + const char *file, + int line, + const char *func, + const char *expr, + const char * const *arg1, + const char * const *arg2, + gsize first_wrong_idx); +#define g_assert_cmpstrv(strv1, strv2) \ + G_STMT_START \ + { \ + const char * const *__strv1 = (const char * const *) (strv1); \ + const char * const *__strv2 = (const char * const *) (strv2); \ + if (!__strv1 || !__strv2) \ + { \ + if (__strv1) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #strv1 " == " #strv2 "): " #strv2 " is NULL, but " #strv1 " is not"); \ + } \ + else if (__strv2) \ + { \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + "assertion failed (" #strv1 " == " #strv2 "): " #strv1 " is NULL, but " #strv2 " is not"); \ + } \ + } \ + else \ + { \ + guint __l1 = g_strv_length ((char **) __strv1); \ + guint __l2 = g_strv_length ((char **) __strv2); \ + if (__l1 != __l2) \ + { \ + char *__msg; \ + __msg = g_strdup_printf ("assertion failed (" #strv1 " == " #strv2 "): length %u does not equal length %u", __l1, __l2); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ + g_free (__msg); \ + } \ + else \ + { \ + guint __i; \ + for (__i = 0; __i < __l1; __i++) \ + { \ + if (g_strcmp0 (__strv1[__i], __strv2[__i]) != 0) \ + { \ + g_assertion_message_cmpstrv (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #strv1 " == " #strv2, \ + __strv1, __strv2, __i); \ + } \ + } \ + } \ + } \ + } \ + G_STMT_END +#endif + +#if !GLIB_CHECK_VERSION (2, 70, 0) +/* Before 2.70, diagnostic messages containing newlines were problematic */ +#define g_test_message(...) _glnx_test_message_safe (__VA_ARGS__) +void _glnx_test_message_safe (const char *format, ...) G_GNUC_PRINTF (1, 2); + +#define g_test_fail_printf _glnx_test_fail_printf +void _glnx_test_fail_printf (const char *format, ...) G_GNUC_PRINTF (1, 2); +#define g_test_skip_printf _glnx_test_skip_printf +void _glnx_test_skip_printf (const char *format, ...) G_GNUC_PRINTF (1, 2); +#define g_test_incomplete_printf _glnx_test_incomplete_printf +void _glnx_test_incomplete_printf (const char *format, ...) G_GNUC_PRINTF (1, 2); +#endif + G_END_DECLS diff --git a/glnx-backports.h b/glnx-backports.h index b535e2df..71c3f5ca 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters + * Copyright 2017 Emmanuele Bassi * SPDX-License-Identifier: LGPL-2.0-or-later * * GLIB - Library of useful routines for C programming @@ -84,4 +85,9 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } #endif +#ifndef G_APPROX_VALUE /* added in 2.58 */ +#define G_APPROX_VALUE(a, b, epsilon) \ + (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) +#endif + G_END_DECLS diff --git a/meson.build b/meson.build index 86535a8a..9b871dbc 100644 --- a/meson.build +++ b/meson.build @@ -57,6 +57,7 @@ libglnx_inc = include_directories('.') libglnx_sources = [ 'glnx-backport-autocleanups.h', 'glnx-backport-autoptr.h', + 'glnx-backport-testutils.c', 'glnx-backport-testutils.h', 'glnx-backports.c', 'glnx-backports.h', diff --git a/tests/meson.build b/tests/meson.build index 2c38ab09..2d0a9767 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -22,11 +22,22 @@ libglnx_testlib_dep = declare_dependency( ) if get_option('tests') + executable( + 'testing-helper', + 'testing-helper.c', + dependencies : [ + libglnx_dep, + libglnx_deps, + ], + install : false, + ) + test_names = [ 'errors', 'fdio', 'macros', 'shutil', + 'testing', 'xattrs', ] diff --git a/tests/test-libglnx-testing.c b/tests/test-libglnx-testing.c new file mode 100644 index 00000000..279e0e11 --- /dev/null +++ b/tests/test-libglnx-testing.c @@ -0,0 +1,361 @@ +/* + * Copyright 2022 Simon McVittie + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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, see + * . + */ + +#include "libglnx-config.h" +#include "libglnx.h" + +#include +#include + +#include +#include +#include +#include + +#if GLIB_CHECK_VERSION (2, 38, 0) +#define GTEST_TAP_OR_VERBOSE "--tap" +#else +#define GTEST_TAP_OR_VERBOSE "--verbose" +#endif + +static const char *null = NULL; +static const char *nonnull = "not null"; + +static void +test_assertions (void) +{ + const char *other_nonnull = "not null"; + g_autoptr(GVariant) va = g_variant_ref_sink (g_variant_new ("i", 42)); + g_autoptr(GVariant) vb = g_variant_ref_sink (g_variant_new ("i", 42)); + const char * const strv1[] = {"one", "two", NULL}; + const char * const strv2[] = {"one", "two", NULL}; + GStatBuf statbuf; + + g_assert_null (null); + g_assert_nonnull (nonnull); + g_assert_cmpmem (null, 0, null, 0); + g_assert_cmpmem (nonnull, strlen (nonnull), other_nonnull, strlen (other_nonnull)); + g_assert_cmpfloat_with_epsilon (1.0, 1.00001, 0.01); + g_assert_cmpvariant (va, vb); + g_assert_no_errno (g_stat ("/", &statbuf)); + g_assert_cmpstrv (NULL, NULL); + g_assert_cmpstrv (&null, &null); + g_assert_cmpstrv (strv1, strv2); +} + +static void +test_assertion_failures (void) +{ + static const char * const assertion_failures[] = + { + "nonnull", + "null", + "mem_null_nonnull", + "mem_nonnull_null", + "mem_len", + "mem_cmp", + "cmpfloat_with_epsilon", + "cmpvariant", + "errno", + "cmpstrv_null_nonnull", + "cmpstrv_nonnull_null", + "cmpstrv_len", + "cmpstrv_cmp", + }; + g_autoptr(GError) error = NULL; + g_autofree char *self = NULL; + g_autofree char *dir = NULL; + g_autofree char *exe = NULL; + gsize i; + + self = glnx_readlinkat_malloc (-1, "/proc/self/exe", NULL, &error); + g_assert_no_error (error); + + dir = g_path_get_dirname (self); + exe = g_build_filename (dir, "testing-helper", NULL); + + for (i = 0; i < G_N_ELEMENTS (assertion_failures); i++) + { + g_autofree char *out = NULL; + g_autofree char *err = NULL; + g_autofree char *name = g_strdup_printf ("/assertion-failure/%s", assertion_failures[i]); + int wait_status = -1; + const char *argv[] = { NULL, "assertion-failures", "-p", NULL, NULL, NULL }; + char *line; + char *saveptr = NULL; + + argv[0] = exe; + argv[3] = name; + argv[4] = GTEST_TAP_OR_VERBOSE; + g_test_message ("%s assertion-failures -p %s %s...", exe, name, GTEST_TAP_OR_VERBOSE); + + g_spawn_sync (NULL, /* cwd */ + (char **) argv, + NULL, /* envp */ + G_SPAWN_DEFAULT, + NULL, /* child setup */ + NULL, /* user data */ + &out, + &err, + &wait_status, + &error); + g_assert_no_error (error); + + g_assert_nonnull (out); + g_assert_nonnull (err); + + for (line = strtok_r (out, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stdout: %s", line); + + saveptr = NULL; + + for (line = strtok_r (err, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stderr: %s", line); + + g_test_message ("wait status: 0x%x", wait_status); + + /* It exited with a nonzero status that was not exit status 77 */ + G_STATIC_ASSERT (WIFEXITED (0)); + G_STATIC_ASSERT (WEXITSTATUS (0) == 0); + g_assert_cmphex (wait_status, !=, 0); + G_STATIC_ASSERT (WIFEXITED (77 << 8)); + G_STATIC_ASSERT (WEXITSTATUS (77 << 8) == 77); + g_assert_cmphex (wait_status, !=, (77 << 8)); + } +} + +static void +test_failures (void) +{ + static const char * const failures[] = + { + "fail", + "fail-printf", + }; + g_autoptr(GError) error = NULL; + g_autofree char *self = NULL; + g_autofree char *dir = NULL; + g_autofree char *exe = NULL; + gsize i; + + self = glnx_readlinkat_malloc (-1, "/proc/self/exe", NULL, &error); + g_assert_no_error (error); + + dir = g_path_get_dirname (self); + exe = g_build_filename (dir, "testing-helper", NULL); + + for (i = 0; i < G_N_ELEMENTS (failures); i++) + { + g_autofree char *out = NULL; + g_autofree char *err = NULL; + int wait_status = -1; + const char *argv[] = { NULL, NULL, NULL, NULL }; + char *line; + char *saveptr; + + argv[0] = exe; + argv[1] = failures[i]; + argv[2] = GTEST_TAP_OR_VERBOSE; + g_test_message ("%s %s %s...", exe, failures[i], GTEST_TAP_OR_VERBOSE); + + g_spawn_sync (NULL, /* cwd */ + (char **) argv, + NULL, /* envp */ + G_SPAWN_DEFAULT, + NULL, /* child setup */ + NULL, /* user data */ + &out, + &err, + &wait_status, + &error); + g_assert_no_error (error); + + for (line = strtok_r (out, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stdout: %s", line); + + for (line = strtok_r (err, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stderr: %s", line); + + g_test_message ("wait status: 0x%x", wait_status); + + G_STATIC_ASSERT (WIFEXITED (0)); + G_STATIC_ASSERT (WEXITSTATUS (0) == 0); + G_STATIC_ASSERT (WIFEXITED (77 << 8)); + G_STATIC_ASSERT (WEXITSTATUS (77 << 8) == 77); + + g_assert_cmphex (wait_status, !=, 0); + g_assert_cmphex (wait_status, !=, (77 << 8)); + } +} + +static void +test_skips (void) +{ + static const char * const skips[] = + { + "skip", + "skip-printf", + "incomplete", + "incomplete-printf", + }; + g_autoptr(GError) error = NULL; + g_autofree char *self = NULL; + g_autofree char *dir = NULL; + g_autofree char *exe = NULL; + gsize i; + + self = glnx_readlinkat_malloc (-1, "/proc/self/exe", NULL, &error); + g_assert_no_error (error); + + dir = g_path_get_dirname (self); + exe = g_build_filename (dir, "testing-helper", NULL); + + for (i = 0; i < G_N_ELEMENTS (skips); i++) + { + g_autofree char *out = NULL; + g_autofree char *err = NULL; + int wait_status = -1; + const char *argv[] = { NULL, NULL, NULL, NULL }; + char *line; + char *saveptr; + + argv[0] = exe; + argv[1] = skips[i]; + argv[2] = GTEST_TAP_OR_VERBOSE; + g_test_message ("%s %s %s...", exe, skips[i], GTEST_TAP_OR_VERBOSE); + + g_spawn_sync (NULL, /* cwd */ + (char **) argv, + NULL, /* envp */ + G_SPAWN_DEFAULT, + NULL, /* child setup */ + NULL, /* user data */ + &out, + &err, + &wait_status, + &error); + g_assert_no_error (error); + + for (line = strtok_r (out, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stdout: %s", line); + + for (line = strtok_r (err, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stderr: %s", line); + + g_test_message ("wait status: 0x%x", wait_status); + + G_STATIC_ASSERT (WIFEXITED (0)); + G_STATIC_ASSERT (WEXITSTATUS (0) == 0); + G_STATIC_ASSERT (WIFEXITED (77 << 8)); + G_STATIC_ASSERT (WEXITSTATUS (77 << 8) == 77); + + /* Ideally the exit status is 77, but it might be 0 with older GLib */ + if (wait_status != 0) + g_assert_cmphex (wait_status, ==, (77 << 8)); + } +} + +static void +test_successes (void) +{ + static const char * const successes[] = + { + "messages", + "pass", + "summary", + }; + g_autoptr(GError) error = NULL; + g_autofree char *self = NULL; + g_autofree char *dir = NULL; + g_autofree char *exe = NULL; + gsize i; + + self = glnx_readlinkat_malloc (-1, "/proc/self/exe", NULL, &error); + g_assert_no_error (error); + + dir = g_path_get_dirname (self); + exe = g_build_filename (dir, "testing-helper", NULL); + + for (i = 0; i < G_N_ELEMENTS (successes); i++) + { + g_autofree char *out = NULL; + g_autofree char *err = NULL; + int wait_status = -1; + const char *argv[] = { NULL, NULL, NULL, NULL }; + char *line; + char *saveptr; + + argv[0] = exe; + argv[1] = successes[i]; + argv[2] = GTEST_TAP_OR_VERBOSE; + g_test_message ("%s %s %s...", exe, successes[i], GTEST_TAP_OR_VERBOSE); + + g_spawn_sync (NULL, /* cwd */ + (char **) argv, + NULL, /* envp */ + G_SPAWN_DEFAULT, + NULL, /* child setup */ + NULL, /* user data */ + &out, + &err, + &wait_status, + &error); + g_assert_no_error (error); + + for (line = strtok_r (out, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stdout: %s", line); + + for (line = strtok_r (err, "\n", &saveptr); + line != NULL; + line = strtok_r (NULL, "\n", &saveptr)) + g_test_message ("stderr: %s", line); + + g_test_message ("wait status: 0x%x", wait_status); + + G_STATIC_ASSERT (WIFEXITED (0)); + G_STATIC_ASSERT (WEXITSTATUS (0) == 0); + g_assert_cmphex (wait_status, ==, 0); + } +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/assertions", test_assertions); + g_test_add_func ("/assertion_failures", test_assertion_failures); + g_test_add_func ("/failures", test_failures); + g_test_add_func ("/skips", test_skips); + g_test_add_func ("/successes", test_successes); + return g_test_run(); +} diff --git a/tests/testing-helper.c b/tests/testing-helper.c new file mode 100644 index 00000000..4e00fbec --- /dev/null +++ b/tests/testing-helper.c @@ -0,0 +1,327 @@ +/* + * Based on glib/tests/testing-helper.c from GLib + * + * Copyright 2018-2022 Collabora Ltd. + * Copyright 2019 Руслан Ижбулатов + * Copyright 2018-2022 Endless OS Foundation LLC + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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, see + * . + */ + +#include "libglnx-config.h" +#include "libglnx.h" + +#include +#include +#include +#include +#include +#include + +static const char *null = NULL; +static const char *nonnull = "not null"; + +static void +test_pass (void) +{ +} + +static void +test_messages (void) +{ + g_test_message ("This message has multiple lines.\n" + "In older GLib, it would corrupt TAP output.\n" + "That's why libglnx provides a wrapper.\n"); +} + +static void +test_assertion_failure_nonnull (void) +{ + g_assert_nonnull (null); +} + +static void +test_assertion_failure_null (void) +{ + g_assert_null (nonnull); +} + +static void +test_assertion_failure_mem_null_nonnull (void) +{ + g_assert_cmpmem (null, 0, nonnull, strlen (nonnull)); +} + +static void +test_assertion_failure_mem_nonnull_null (void) +{ + g_assert_cmpmem (nonnull, strlen (nonnull), null, 0); +} + +static void +test_assertion_failure_mem_len (void) +{ + g_assert_cmpmem (nonnull, strlen (nonnull), nonnull, 0); +} + +static void +test_assertion_failure_mem_cmp (void) +{ + g_assert_cmpmem (nonnull, 4, nonnull + 4, 4); +} + +static void +test_assertion_failure_cmpfloat_with_epsilon (void) +{ + g_assert_cmpfloat_with_epsilon (1.0, 1.5, 0.001); +} + +static void +test_assertion_failure_cmpvariant (void) +{ + g_autoptr(GVariant) a = g_variant_ref_sink (g_variant_new ("i", 42)); + g_autoptr(GVariant) b = g_variant_ref_sink (g_variant_new ("u", 42)); + + g_assert_cmpvariant (a, b); +} + +static void +test_assertion_failure_errno (void) +{ + g_assert_no_errno (mkdir ("/", 0755)); +} + +static void +test_assertion_failure_cmpstrv_null_nonnull (void) +{ + const char * const b[] = { NULL }; + + g_assert_cmpstrv (NULL, b); +} + +static void +test_assertion_failure_cmpstrv_nonnull_null (void) +{ + const char * const a[] = { NULL }; + + g_assert_cmpstrv (a, NULL); +} + +static void +test_assertion_failure_cmpstrv_len (void) +{ + const char * const a[] = { "one", NULL }; + const char * const b[] = { NULL }; + + g_assert_cmpstrv (a, b); +} + +static void +test_assertion_failure_cmpstrv_cmp (void) +{ + const char * const a[] = { "one", "two", NULL }; + const char * const b[] = { "one", "three", NULL }; + + g_assert_cmpstrv (a, b); +} + +static void +test_skip (void) +{ + g_test_skip ("not enough tea"); +} + +static void +test_skip_printf (void) +{ + const char *beverage = "coffee"; + + g_test_skip_printf ("not enough %s", beverage); +} + +static void +test_fail (void) +{ + g_test_fail (); +} + +static void +test_fail_printf (void) +{ + g_test_fail_printf ("this test intentionally left failing"); +} + +static void +test_incomplete (void) +{ + g_test_incomplete ("mind reading not implemented yet"); +} + +static void +test_incomplete_printf (void) +{ + const char *operation = "telekinesis"; + + g_test_incomplete_printf ("%s not implemented yet", operation); +} + +static void +test_summary (void) +{ + g_test_summary ("Tests that g_test_summary() works with TAP, by outputting a " + "known summary message in testing-helper, and checking for " + "it in the TAP output later."); +} + +int +main (int argc, + char *argv[]) +{ + char *argv1; + + setlocale (LC_ALL, ""); + +#ifdef G_OS_WIN32 + /* Windows opens std streams in text mode, with \r\n EOLs. + * Sometimes it's easier to force a switch to binary mode than + * to account for extra \r in testcases. + */ + setmode (fileno (stdout), O_BINARY); +#endif + + g_return_val_if_fail (argc > 1, 1); + argv1 = argv[1]; + + if (argc > 2) + memmove (&argv[1], &argv[2], (argc - 2) * sizeof (char *)); + + argc -= 1; + argv[argc] = NULL; + + if (g_strcmp0 (argv1, "init-null-argv0") == 0) + { + int test_argc = 0; + char *test_argva[1] = { NULL }; + char **test_argv = test_argva; + + /* Test that `g_test_init()` can handle being called with an empty argv + * and argc == 0. While this isn’t recommended, it is possible for another + * process to use execve() to call a gtest process this way, so we’d + * better handle it gracefully. + * + * This test can’t be run after `g_test_init()` has been called normally, + * as it isn’t allowed to be called more than once in a process. */ + g_test_init (&test_argc, &test_argv, NULL); + + return 0; + } + + g_test_init (&argc, &argv, NULL); +#if GLIB_CHECK_VERSION(2, 38, 0) + g_test_set_nonfatal_assertions (); +#endif + + if (g_strcmp0 (argv1, "pass") == 0) + { + g_test_add_func ("/pass", test_pass); + } + else if (g_strcmp0 (argv1, "messages") == 0) + { + g_test_add_func ("/messages", test_messages); + } + else if (g_strcmp0 (argv1, "skip") == 0) + { + g_test_add_func ("/skip", test_skip); + } + else if (g_strcmp0 (argv1, "skip-printf") == 0) + { + g_test_add_func ("/skip-printf", test_skip_printf); + } + else if (g_strcmp0 (argv1, "incomplete") == 0) + { + g_test_add_func ("/incomplete", test_incomplete); + } + else if (g_strcmp0 (argv1, "incomplete-printf") == 0) + { + g_test_add_func ("/incomplete-printf", test_incomplete_printf); + } + else if (g_strcmp0 (argv1, "fail") == 0) + { + g_test_add_func ("/fail", test_fail); + } + else if (g_strcmp0 (argv1, "fail-printf") == 0) + { + g_test_add_func ("/fail-printf", test_fail_printf); + } + else if (g_strcmp0 (argv1, "all-non-failures") == 0) + { + g_test_add_func ("/pass", test_pass); + g_test_add_func ("/skip", test_skip); + g_test_add_func ("/incomplete", test_incomplete); + } + else if (g_strcmp0 (argv1, "all") == 0) + { + g_test_add_func ("/pass", test_pass); + g_test_add_func ("/skip", test_skip); + g_test_add_func ("/incomplete", test_incomplete); + g_test_add_func ("/fail", test_fail); + } + else if (g_strcmp0 (argv1, "skip-options") == 0) + { + /* The caller is expected to skip some of these with + * -p/-r, -s/-x and/or --GTestSkipCount */ + g_test_add_func ("/a", test_pass); + g_test_add_func ("/b", test_pass); + g_test_add_func ("/b/a", test_pass); + g_test_add_func ("/b/b", test_pass); + g_test_add_func ("/b/b/a", test_pass); + g_test_add_func ("/prefix/a", test_pass); + g_test_add_func ("/prefix/b/b", test_pass); + g_test_add_func ("/prefix-long/a", test_pass); + g_test_add_func ("/c/a", test_pass); + g_test_add_func ("/d/a", test_pass); + } + else if (g_strcmp0 (argv1, "summary") == 0) + { + g_test_add_func ("/summary", test_summary); + } + else if (g_strcmp0 (argv1, "assertion-failures") == 0) + { + /* Use -p to select a specific one of these */ +#define T(x) g_test_add_func ("/assertion-failure/" #x, test_assertion_failure_ ## x) + T (nonnull); + T (null); + T (mem_null_nonnull); + T (mem_nonnull_null); + T (mem_len); + T (mem_cmp); + T (cmpfloat_with_epsilon); + T (cmpvariant); + T (errno); + T (cmpstrv_null_nonnull); + T (cmpstrv_nonnull_null); + T (cmpstrv_len); + T (cmpstrv_cmp); +#undef T + } + else + { + g_assert_not_reached (); + } + + return g_test_run (); +} From 45108f1852d329b3a4d10f6d5d3558dca8288dcb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 10 Oct 2022 14:08:54 +0100 Subject: [PATCH 261/280] build: Explicitly disable warnings for non-ISO C features libglnx is intentionally not portable to non-Unix platforms or to compilers that do not implement gcc/clang extensions, so it's counterproductive to warn about these extensions, even if libglnx is used by a parent project that generally (for the parts that don't use libglnx) wants to be portable to any ISO C compiler. Suggested by Will Thompson on !36. Signed-off-by: Simon McVittie --- meson.build | 5 +++++ tests/use-as-subproject/meson.build | 28 ++++++++++++++++++++++++++-- tests/use-as-subproject/trivial.c | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/use-as-subproject/trivial.c diff --git a/meson.build b/meson.build index 38ec2ea7..db605335 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,11 @@ project( add_project_arguments('-D_GNU_SOURCE', language: 'c') add_project_arguments('-Wno-unused-local-typedefs', language: 'c') +# We are intentionally using non-ISO features in this (sub)project, +# even if a parent project wants to use pedantic warnings +add_project_arguments('-Wno-pedantic', language: 'c') +add_project_arguments('-Wno-variadic-macros', language: 'c') + cc = meson.get_compiler('c') diff --git a/tests/use-as-subproject/meson.build b/tests/use-as-subproject/meson.build index 2d081604..59fd736a 100644 --- a/tests/use-as-subproject/meson.build +++ b/tests/use-as-subproject/meson.build @@ -4,6 +4,10 @@ project( 'use-libglnx-as-subproject', 'c', + default_options : [ + 'c_std=gnu99', + 'warning_level=3', + ], version : '0', meson_version : '>=0.49.0', ) @@ -20,5 +24,25 @@ libglnx = subproject('libglnx') libglnx_dep = libglnx.get_variable('libglnx_dep') libglnx_testlib_dep = libglnx.get_variable('libglnx_testlib_dep') -executable('use-libglnx', 'use-libglnx.c', dependencies : [libglnx_dep, glib_dep]) -executable('use-testlib', 'use-testlib.c', dependencies : [libglnx_testlib_dep, glib_dep]) +# This executable is compiled at warning_level=3 by default +executable( + 'trivial', + 'trivial.c', + dependencies : [glib_dep], +) + +# These can't be compiled at warning_level=3 because they use non-ISO +# compiler features in the libglnx headers, which would be warnings or +# errors with -Wpedantic +executable( + 'use-libglnx', + 'use-libglnx.c', + dependencies : [libglnx_dep, glib_dep], + override_options : ['warning_level=2'], +) +executable( + 'use-testlib', + 'use-testlib.c', + dependencies : [libglnx_testlib_dep, glib_dep], + override_options : ['warning_level=2'], +) diff --git a/tests/use-as-subproject/trivial.c b/tests/use-as-subproject/trivial.c new file mode 100644 index 00000000..4b364f1a --- /dev/null +++ b/tests/use-as-subproject/trivial.c @@ -0,0 +1,15 @@ +/* + * Copyright 2022 Collabora Ltd. + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include + +int +main (void) +{ + GError *error = NULL; + + g_clear_error (&error); + return 0; +} From cdef2bb414ec05aa9bb4bf7cc15743d643eef6c3 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 10 Oct 2022 14:34:25 +0100 Subject: [PATCH 262/280] backports: Use #ifndef instead of GLIB_CHECK_VERSION As Will Thompson pointed out on !37, G_OPTION_ENTRY_NULL is a macro in GLib, so we can test for it with `#ifndef` rather than a version guard. This is a little bit nicer for parent projects that might already have their own backport of it. Signed-off-by: Simon McVittie --- glnx-backports.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-backports.h b/glnx-backports.h index d1098331..0a0a9da2 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -84,7 +84,7 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_DBUS_METHOD_INVOCATION_UNHANDLED FALSE #endif -#if !GLIB_CHECK_VERSION(2, 70, 0) +#ifndef G_OPTION_ENTRY_NULL /* added in 2.70 */ #define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } #endif From 5bb5b7c71ed917e55aea06171af7fb1b95dae817 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 10 Oct 2022 14:38:25 +0100 Subject: [PATCH 263/280] backports: Backport new NONE/DEFAULT constants from GLib 2.74 These enums were not originally defined with a zero-valued constant (or in the case of GApplicationFlags, the constant always existed but its name was inappropriate for GObject-Introspection), and the corresponding constants were added in GLib 2.74 to make them more self-documenting. Signed-off-by: Simon McVittie --- glnx-backports.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/glnx-backports.h b/glnx-backports.h index 68ce025b..184a6d1e 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -94,4 +94,17 @@ gboolean glnx_set_object (GObject **object_ptr, (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) #endif +#if !GLIB_CHECK_VERSION(2, 74, 0) +#define G_APPLICATION_DEFAULT_FLAGS ((GApplicationFlags) 0) +#define G_CONNECT_DEFAULT ((GConnectFlags) 0) +#define G_IO_FLAG_NONE ((GIOFlags) 0) +#define G_MARKUP_DEFAULT_FLAGS ((GMarkupParseFlags) 0) +#define G_REGEX_DEFAULT ((GRegexCompileFlags) 0) +#define G_REGEX_MATCH_DEFAULT ((GRegexMatchFlags) 0) +#define G_TEST_SUBPROCESS_DEFAULT ((GTestSubprocessFlags) 0) +#define G_TEST_TRAP_DEFAULT ((GTestTrapFlags) 0) +#define G_TLS_CERTIFICATE_NO_FLAGS ((GTlsCertificateFlags) 0) +#define G_TYPE_FLAG_NONE ((GTypeFlags) 0) +#endif + G_END_DECLS From a91959e0d45cd624846d2f83edf0d10bae9fc2d0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 10 Oct 2022 15:27:56 +0100 Subject: [PATCH 264/280] backport-testutils: Add g_assert_true(), g_assert_false() These were only added in 2.38, and some projects with particularly ancient dependencies (like the Steam Runtime, based on a 2012 version of Ubuntu) still avoid depending on that version. Signed-off-by: Simon McVittie --- glnx-backport-testutils.h | 8 ++++++++ tests/test-libglnx-testing.c | 4 ++++ tests/testing-helper.c | 14 ++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/glnx-backport-testutils.h b/glnx-backport-testutils.h index 2febcc0c..9c17f06e 100644 --- a/glnx-backport-testutils.h +++ b/glnx-backport-testutils.h @@ -34,6 +34,14 @@ G_BEGIN_DECLS +#ifndef g_assert_true /* added in 2.38 */ +#define g_assert_true(x) g_assert ((x)) +#endif + +#ifndef g_assert_false /* added in 2.38 */ +#define g_assert_false(x) g_assert (!(x)) +#endif + #ifndef g_assert_nonnull /* added in 2.40 */ #define g_assert_nonnull(x) g_assert (x != NULL) #endif diff --git a/tests/test-libglnx-testing.c b/tests/test-libglnx-testing.c index 279e0e11..aef6edae 100644 --- a/tests/test-libglnx-testing.c +++ b/tests/test-libglnx-testing.c @@ -47,6 +47,8 @@ test_assertions (void) const char * const strv2[] = {"one", "two", NULL}; GStatBuf statbuf; + g_assert_true (null == NULL); + g_assert_false (null != NULL); g_assert_null (null); g_assert_nonnull (nonnull); g_assert_cmpmem (null, 0, null, 0); @@ -64,6 +66,8 @@ test_assertion_failures (void) { static const char * const assertion_failures[] = { + "true", + "false", "nonnull", "null", "mem_null_nonnull", diff --git a/tests/testing-helper.c b/tests/testing-helper.c index 4e00fbec..7c2192cf 100644 --- a/tests/testing-helper.c +++ b/tests/testing-helper.c @@ -48,6 +48,18 @@ test_messages (void) "That's why libglnx provides a wrapper.\n"); } +static void +test_assertion_failure_true (void) +{ + g_assert_true (null != NULL); +} + +static void +test_assertion_failure_false (void) +{ + g_assert_false (null == NULL); +} + static void test_assertion_failure_nonnull (void) { @@ -303,6 +315,8 @@ main (int argc, { /* Use -p to select a specific one of these */ #define T(x) g_test_add_func ("/assertion-failure/" #x, test_assertion_failure_ ## x) + T (true); + T (false); T (nonnull); T (null); T (mem_null_nonnull); From 416744a26533b7beabb32b0af3042ea32f808d0d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 10 Oct 2022 15:35:11 +0100 Subject: [PATCH 265/280] tests: Ensure saveptr is NULL before first call to strtok_r() The standards-conformant behaviour is that the contents of saveptr are ignored when the first argument is non-NULL, but the man page notes that some older implementations required saveptr to be NULL in that situation, and the gcc 4.8 provided by the Steam Runtime warns about this. Signed-off-by: Simon McVittie --- tests/test-libglnx-testing.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test-libglnx-testing.c b/tests/test-libglnx-testing.c index 279e0e11..84351133 100644 --- a/tests/test-libglnx-testing.c +++ b/tests/test-libglnx-testing.c @@ -171,7 +171,7 @@ test_failures (void) int wait_status = -1; const char *argv[] = { NULL, NULL, NULL, NULL }; char *line; - char *saveptr; + char *saveptr = NULL; argv[0] = exe; argv[1] = failures[i]; @@ -195,6 +195,8 @@ test_failures (void) line = strtok_r (NULL, "\n", &saveptr)) g_test_message ("stdout: %s", line); + saveptr = NULL; + for (line = strtok_r (err, "\n", &saveptr); line != NULL; line = strtok_r (NULL, "\n", &saveptr)) @@ -241,7 +243,7 @@ test_skips (void) int wait_status = -1; const char *argv[] = { NULL, NULL, NULL, NULL }; char *line; - char *saveptr; + char *saveptr = NULL; argv[0] = exe; argv[1] = skips[i]; @@ -265,6 +267,8 @@ test_skips (void) line = strtok_r (NULL, "\n", &saveptr)) g_test_message ("stdout: %s", line); + saveptr = NULL; + for (line = strtok_r (err, "\n", &saveptr); line != NULL; line = strtok_r (NULL, "\n", &saveptr)) @@ -311,7 +315,7 @@ test_successes (void) int wait_status = -1; const char *argv[] = { NULL, NULL, NULL, NULL }; char *line; - char *saveptr; + char *saveptr = NULL; argv[0] = exe; argv[1] = successes[i]; @@ -335,6 +339,8 @@ test_successes (void) line = strtok_r (NULL, "\n", &saveptr)) g_test_message ("stdout: %s", line); + saveptr = NULL; + for (line = strtok_r (err, "\n", &saveptr); line != NULL; line = strtok_r (NULL, "\n", &saveptr)) From 8a29325374fcc2dc847d65374d63b4401fc270a0 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 24 Oct 2022 11:58:23 +0100 Subject: [PATCH 266/280] backports: Add a backport of g_memdup2() g_memdup2() replaces g_memdup(), which is prone to integer overflow on 64-bit systems if copying a very large object with an attacker-controlled size. The original version in GLib is extern, but it seems simple enough to inline a backport. Related: https://gitlab.gnome.org/GNOME/glib/-/issues/2319 Signed-off-by: Simon McVittie --- glnx-backports.h | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/glnx-backports.h b/glnx-backports.h index afab3927..f99f08fb 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -1,8 +1,10 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * + * Copyright 1998 Manish Singh + * Copyright 1998 Tim Janik * Copyright (C) 2015 Colin Walters * Copyright 2017 Emmanuele Bassi - * SPDX-License-Identifier: LGPL-2.0-or-later + * SPDX-License-Identifier: LGPL-2.1-or-later * * GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald @@ -10,7 +12,7 @@ * 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 of the License, or (at your option) any later version. + * 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 @@ -18,13 +20,13 @@ * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ #pragma once +#include + #include G_BEGIN_DECLS @@ -85,6 +87,28 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_DBUS_METHOD_INVOCATION_UNHANDLED FALSE #endif +#if !GLIB_CHECK_VERSION(2, 68, 0) +static inline gpointer _glnx_memdup2 (gconstpointer mem, + gsize byte_size) G_GNUC_ALLOC_SIZE(2); +static inline gpointer +_glnx_memdup2 (gconstpointer mem, + gsize byte_size) +{ + gpointer new_mem; + + if (mem && byte_size != 0) + { + new_mem = g_malloc (byte_size); + memcpy (new_mem, mem, byte_size); + } + else + new_mem = NULL; + + return new_mem; +} +#define g_memdup2 _glnx_memdup2 +#endif + #ifndef G_OPTION_ENTRY_NULL /* added in 2.70 */ #define G_OPTION_ENTRY_NULL { NULL, 0, 0, 0, NULL, NULL, NULL } #endif From 5275410e6c61d520b8c4b8617faba46a37c65ca1 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 24 Oct 2022 12:10:25 +0100 Subject: [PATCH 267/280] tests: Add a simple test for g_memdup2, from GLib Signed-off-by: Simon McVittie --- LICENSES/LicenseRef-old-glib-tests.txt | 16 ++++++++++++ tests/meson.build | 1 + tests/test-libglnx-backports.c | 36 ++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 LICENSES/LicenseRef-old-glib-tests.txt create mode 100644 tests/test-libglnx-backports.c diff --git a/LICENSES/LicenseRef-old-glib-tests.txt b/LICENSES/LicenseRef-old-glib-tests.txt new file mode 100644 index 00000000..d78b4430 --- /dev/null +++ b/LICENSES/LicenseRef-old-glib-tests.txt @@ -0,0 +1,16 @@ +This work is provided "as is"; redistribution and modification +in whole or in part, in any medium, physical or electronic is +permitted without restriction. + +This work 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. + +In no event shall the authors or contributors be liable for any +direct, indirect, incidental, special, exemplary, or consequential +damages (including, but not limited to, procurement of substitute +goods or services; loss of use, data, or profits; or business +interruption) however caused and on any theory of liability, whether +in contract, strict liability, or tort (including negligence or +otherwise) arising in any way out of the use of this software, even +if advised of the possibility of such damage. diff --git a/tests/meson.build b/tests/meson.build index 2d0a9767..2d32a233 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -33,6 +33,7 @@ if get_option('tests') ) test_names = [ + 'backports', 'errors', 'fdio', 'macros', diff --git a/tests/test-libglnx-backports.c b/tests/test-libglnx-backports.c new file mode 100644 index 00000000..c475cd4c --- /dev/null +++ b/tests/test-libglnx-backports.c @@ -0,0 +1,36 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright 2019 Emmanuel Fleury + * SPDX-License-Identifier: LGPL-2.1-or-later AND LicenseRef-old-glib-tests + */ + +#include "libglnx-config.h" +#include "libglnx.h" + +/* Testing g_memdup2() function with various positive and negative cases */ +static void +test_memdup2 (void) +{ + gchar *str_dup = NULL; + const gchar *str = "The quick brown fox jumps over the lazy dog"; + + /* Testing negative cases */ + g_assert_null (g_memdup2 (NULL, 1024)); + g_assert_null (g_memdup2 (str, 0)); + g_assert_null (g_memdup2 (NULL, 0)); + + /* Testing normal usage cases */ + str_dup = g_memdup2 (str, strlen (str) + 1); + g_assert_nonnull (str_dup); + g_assert_cmpstr (str, ==, str_dup); + + g_free (str_dup); +} + +int main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/strfuncs/memdup2", test_memdup2); + return g_test_run(); +} From ea18312ed03e0077740e327966a8e0e5810d7f5b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 8 Nov 2022 11:05:12 +0000 Subject: [PATCH 268/280] backports: Add g_steal_fd, from GLib >= 2.70 This is essentially the same as glnx_steal_fd, so make glnx_steal_fd an alias for it. The unit test is taken from GLib, slightly modified to avoid g_close() and also test the old name glnx_steal_fd(). Signed-off-by: Simon McVittie --- glnx-backports.h | 11 ++++++++++ glnx-dirfd.c | 4 ++-- glnx-fdio.c | 6 +++--- glnx-local-alloc.h | 15 ++++++------- glnx-lockfile.c | 2 +- tests/test-libglnx-backports.c | 39 ++++++++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/glnx-backports.h b/glnx-backports.h index f99f08fb..ad70ed90 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -118,6 +118,17 @@ _glnx_memdup2 (gconstpointer mem, (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) #endif +#if !GLIB_CHECK_VERSION(2, 70, 0) +#define g_steal_fd _glnx_steal_fd +static inline int +_glnx_steal_fd (int *fdp) +{ + int fd = *fdp; + *fdp = -1; + return fd; +} +#endif + #if !GLIB_CHECK_VERSION(2, 74, 0) #define G_APPLICATION_DEFAULT_FLAGS ((GApplicationFlags) 0) #define G_CONNECT_DEFAULT ((GConnectFlags) 0) diff --git a/glnx-dirfd.c b/glnx-dirfd.c index b039c419..b78e2dfb 100644 --- a/glnx-dirfd.c +++ b/glnx-dirfd.c @@ -129,7 +129,7 @@ glnx_dirfd_iterator_init_take_fd (int *dfd, if (!d) return glnx_throw_errno_prefix (error, "fdopendir"); - real_dfd_iter->fd = glnx_steal_fd (dfd); + real_dfd_iter->fd = g_steal_fd (dfd); real_dfd_iter->d = d; real_dfd_iter->initialized = TRUE; @@ -349,7 +349,7 @@ glnx_mkdtempat (int dfd, const char *tmpl, int mode, /* Return the initialized directory struct */ out_tmpdir->initialized = TRUE; out_tmpdir->src_dfd = dfd; /* referenced; see above docs */ - out_tmpdir->fd = glnx_steal_fd (&ret_dfd); + out_tmpdir->fd = g_steal_fd (&ret_dfd); out_tmpdir->path = g_steal_pointer (&path); return TRUE; } diff --git a/glnx-fdio.c b/glnx-fdio.c index b1af6793..98732051 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -218,7 +218,7 @@ open_tmpfile_core (int dfd, const char *subpath, return glnx_throw_errno_prefix (error, "fchmod"); out_tmpf->initialized = TRUE; out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ - out_tmpf->fd = glnx_steal_fd (&fd); + out_tmpf->fd = g_steal_fd (&fd); out_tmpf->path = NULL; return TRUE; } @@ -245,7 +245,7 @@ open_tmpfile_core (int dfd, const char *subpath, { out_tmpf->initialized = TRUE; out_tmpf->src_dfd = dfd; /* Copied; caller must keep open */ - out_tmpf->fd = glnx_steal_fd (&fd); + out_tmpf->fd = g_steal_fd (&fd); out_tmpf->path = g_steal_pointer (&tmp); return TRUE; } @@ -463,7 +463,7 @@ glnx_tmpfile_reopen_rdonly (GLnxTmpfile *tmpf, } glnx_close_fd (&tmpf->fd); - tmpf->fd = glnx_steal_fd (&rdonly_fd); + tmpf->fd = g_steal_fd (&rdonly_fd); return TRUE; } diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index 615b9548..65ae747f 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -24,6 +24,8 @@ #include #include +#include "glnx-backports.h" + G_BEGIN_DECLS /** @@ -43,19 +45,14 @@ glnx_local_obj_unref (void *v) } #define glnx_unref_object __attribute__ ((cleanup(glnx_local_obj_unref))) -static inline int -glnx_steal_fd (int *fdp) -{ - int fd = *fdp; - *fdp = -1; - return fd; -} +/* Backwards-compat with older libglnx */ +#define glnx_steal_fd g_steal_fd /** * glnx_close_fd: * @fdp: Pointer to fd * - * Effectively `close (glnx_steal_fd (&fd))`. Also + * Effectively `close (g_steal_fd (&fd))`. Also * asserts that `close()` did not raise `EBADF` - encountering * that error is usually a critical bug in the program. */ @@ -66,7 +63,7 @@ glnx_close_fd (int *fdp) g_assert (fdp); - int fd = glnx_steal_fd (fdp); + int fd = g_steal_fd (fdp); if (fd >= 0) { errsv = errno; diff --git a/glnx-lockfile.c b/glnx-lockfile.c index 65a15585..fcda84cf 100644 --- a/glnx-lockfile.c +++ b/glnx-lockfile.c @@ -129,7 +129,7 @@ glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_loc out_lock->initialized = TRUE; out_lock->dfd = dfd; out_lock->path = g_steal_pointer (&t); - out_lock->fd = glnx_steal_fd (&fd); + out_lock->fd = g_steal_fd (&fd); out_lock->operation = operation; return TRUE; } diff --git a/tests/test-libglnx-backports.c b/tests/test-libglnx-backports.c index c475cd4c..0fa374e5 100644 --- a/tests/test-libglnx-backports.c +++ b/tests/test-libglnx-backports.c @@ -2,12 +2,15 @@ * * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * Copyright 2019 Emmanuel Fleury + * Copyright 2021 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.1-or-later AND LicenseRef-old-glib-tests */ #include "libglnx-config.h" #include "libglnx.h" +#include + /* Testing g_memdup2() function with various positive and negative cases */ static void test_memdup2 (void) @@ -28,9 +31,45 @@ test_memdup2 (void) g_free (str_dup); } +static void +test_steal_fd (void) +{ + GError *error = NULL; + gchar *tmpfile = NULL; + int fd = -42; + int borrowed; + int stolen; + + g_assert_cmpint (g_steal_fd (&fd), ==, -42); + g_assert_cmpint (fd, ==, -1); + g_assert_cmpint (g_steal_fd (&fd), ==, -1); + g_assert_cmpint (fd, ==, -1); + + fd = g_file_open_tmp (NULL, &tmpfile, &error); + g_assert_cmpint (fd, >=, 0); + g_assert_no_error (error); + borrowed = fd; + stolen = g_steal_fd (&fd); + g_assert_cmpint (fd, ==, -1); + g_assert_cmpint (borrowed, ==, stolen); + + g_assert_no_errno (close (g_steal_fd (&stolen))); + g_assert_cmpint (stolen, ==, -1); + + g_assert_no_errno (remove (tmpfile)); + g_free (tmpfile); + + /* Backwards compatibility with older libglnx: glnx_steal_fd is the same + * as g_steal_fd */ + fd = -23; + g_assert_cmpint (glnx_steal_fd (&fd), ==, -23); + g_assert_cmpint (fd, ==, -1); +} + int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); + g_test_add_func ("/mainloop/steal-fd", test_steal_fd); g_test_add_func ("/strfuncs/memdup2", test_memdup2); return g_test_run(); } From 52f66d482b1d721c15e705896b329419f8ef2007 Mon Sep 17 00:00:00 2001 From: Ludovico de Nittis Date: Wed, 1 Mar 2023 09:42:00 +0100 Subject: [PATCH 269/280] backports: Add g_strv_equal from GLib >= 2.60 This is a very useful utility function that allows you to compare two strings arrays. Signed-off-by: Ludovico de Nittis --- glnx-backports.c | 22 ++++++++++++++++++++++ glnx-backports.h | 7 +++++++ tests/test-libglnx-backports.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/glnx-backports.c b/glnx-backports.c index f9aa7ce5..391c154b 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters + * Copyright (C) 2018 Endless OS Foundation, LLC * SPDX-License-Identifier: LGPL-2.0-or-later * * This program is free software: you can redistribute it and/or modify @@ -60,3 +61,24 @@ glnx_set_object (GObject **object_ptr, return TRUE; } #endif + +#if !GLIB_CHECK_VERSION(2, 60, 0) +gboolean +_glnx_strv_equal (const gchar * const *strv1, + const gchar * const *strv2) +{ + g_return_val_if_fail (strv1 != NULL, FALSE); + g_return_val_if_fail (strv2 != NULL, FALSE); + + if (strv1 == strv2) + return TRUE; + + for (; *strv1 != NULL && *strv2 != NULL; strv1++, strv2++) + { + if (!g_str_equal (*strv1, *strv2)) + return FALSE; + } + + return (*strv1 == NULL && *strv2 == NULL); +} +#endif diff --git a/glnx-backports.h b/glnx-backports.h index ad70ed90..121e0731 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -3,6 +3,7 @@ * Copyright 1998 Manish Singh * Copyright 1998 Tim Janik * Copyright (C) 2015 Colin Walters + * Copyright (C) 2018 Endless OS Foundation, LLC * Copyright 2017 Emmanuele Bassi * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -79,6 +80,12 @@ gboolean glnx_set_object (GObject **object_ptr, #define G_OPTION_FLAG_NONE ((GOptionFlags) 0) #endif +#if !GLIB_CHECK_VERSION(2, 60, 0) +#define g_strv_equal _glnx_strv_equal +gboolean _glnx_strv_equal (const gchar * const *strv1, + const gchar * const *strv2); +#endif + #ifndef G_DBUS_METHOD_INVOCATION_HANDLED /* added in 2.68 */ #define G_DBUS_METHOD_INVOCATION_HANDLED TRUE #endif diff --git a/tests/test-libglnx-backports.c b/tests/test-libglnx-backports.c index 0fa374e5..3683791d 100644 --- a/tests/test-libglnx-backports.c +++ b/tests/test-libglnx-backports.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 2018 Endless OS Foundation, LLC * Copyright 2019 Emmanuel Fleury * Copyright 2021 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.1-or-later AND LicenseRef-old-glib-tests @@ -66,10 +67,39 @@ test_steal_fd (void) g_assert_cmpint (fd, ==, -1); } +/* Test g_strv_equal() works for various inputs. */ +static void +test_strv_equal (void) +{ + const gchar *strv_empty[] = { NULL }; + const gchar *strv_empty2[] = { NULL }; + const gchar *strv_simple[] = { "hello", "you", NULL }; + const gchar *strv_simple2[] = { "hello", "you", NULL }; + const gchar *strv_simple_reordered[] = { "you", "hello", NULL }; + const gchar *strv_simple_superset[] = { "hello", "you", "again", NULL }; + const gchar *strv_another[] = { "not", "a", "coded", "message", NULL }; + + g_assert_true (g_strv_equal (strv_empty, strv_empty)); + g_assert_true (g_strv_equal (strv_empty, strv_empty2)); + g_assert_true (g_strv_equal (strv_empty2, strv_empty)); + g_assert_false (g_strv_equal (strv_empty, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_empty)); + g_assert_true (g_strv_equal (strv_simple, strv_simple)); + g_assert_true (g_strv_equal (strv_simple, strv_simple2)); + g_assert_true (g_strv_equal (strv_simple2, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_simple_reordered)); + g_assert_false (g_strv_equal (strv_simple_reordered, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_simple_superset)); + g_assert_false (g_strv_equal (strv_simple_superset, strv_simple)); + g_assert_false (g_strv_equal (strv_simple, strv_another)); + g_assert_false (g_strv_equal (strv_another, strv_simple)); +} + int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/mainloop/steal-fd", test_steal_fd); g_test_add_func ("/strfuncs/memdup2", test_memdup2); + g_test_add_func ("/strfuncs/strv-equal", test_strv_equal); return g_test_run(); } From 30b89b830d7e88c47d704b980cb9749bb61f8afb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 28 Jul 2023 14:50:06 +0100 Subject: [PATCH 270/280] testutils: Add a backport of g_test_disable_crash_reporting() When testing something that is expected to fail or crash, it's useful to disable core dump reporting. This is a slightly simplified version of GNOME/glib!3510: because libglnx isn't portable to non-Linux, we can assume that , prctl() and are always available, so we don't need to check for them in the build system. Signed-off-by: Simon McVittie --- glnx-backport-testutils.c | 17 +++++++++++++++++ glnx-backport-testutils.h | 5 +++++ tests/testing-helper.c | 1 + 3 files changed, 23 insertions(+) diff --git a/glnx-backport-testutils.c b/glnx-backport-testutils.c index 31105c90..3440872d 100644 --- a/glnx-backport-testutils.c +++ b/glnx-backport-testutils.c @@ -33,6 +33,9 @@ #include "glnx-backport-testutils.h" #include "glnx-backports.h" +#include +#include + #if !GLIB_CHECK_VERSION (2, 68, 0) /* Backport of g_assertion_message_cmpstrv() */ void @@ -143,3 +146,17 @@ _glnx_test_incomplete_printf (const char *format, #endif } #endif + +#if !GLIB_CHECK_VERSION (2, 78, 0) +void +_glnx_test_disable_crash_reporting (void) +{ + struct rlimit limit = { 0, 0 }; + + (void) setrlimit (RLIMIT_CORE, &limit); + + /* On Linux, RLIMIT_CORE = 0 is ignored if core dumps are + * configured to be written to a pipe, but PR_SET_DUMPABLE is not. */ + (void) prctl (PR_SET_DUMPABLE, 0, 0, 0, 0); +} +#endif diff --git a/glnx-backport-testutils.h b/glnx-backport-testutils.h index 9c17f06e..f579c0eb 100644 --- a/glnx-backport-testutils.h +++ b/glnx-backport-testutils.h @@ -201,4 +201,9 @@ void _glnx_test_skip_printf (const char *format, ...) G_GNUC_PRINTF (1, 2); void _glnx_test_incomplete_printf (const char *format, ...) G_GNUC_PRINTF (1, 2); #endif +#if !GLIB_CHECK_VERSION (2, 78, 0) +#define g_test_disable_crash_reporting _glnx_test_disable_crash_reporting +void _glnx_test_disable_crash_reporting (void); +#endif + G_END_DECLS diff --git a/tests/testing-helper.c b/tests/testing-helper.c index 7c2192cf..0886678f 100644 --- a/tests/testing-helper.c +++ b/tests/testing-helper.c @@ -243,6 +243,7 @@ main (int argc, } g_test_init (&argc, &argv, NULL); + g_test_disable_crash_reporting (); #if GLIB_CHECK_VERSION(2, 38, 0) g_test_set_nonfatal_assertions (); #endif From 1bd99c1b5102824f11dd25241e4cffead786dde6 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 29 Aug 2023 19:50:28 +0100 Subject: [PATCH 271/280] build: Make tests depend on the helper required by one of them This allows doing: meson setup _build meson test -C _build without an intervening `meson compile` step. Previously, this would have failed in `tests/testing` because `testing-helper` was never compiled. For a project this small, there's no real need to distinguish precisely which tests need the helper: we can have a simpler build by just assuming that they all do, like Autotools would. Signed-off-by: Simon McVittie --- tests/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 2d32a233..6c46b45c 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -22,7 +22,7 @@ libglnx_testlib_dep = declare_dependency( ) if get_option('tests') - executable( + testing_helper = executable( 'testing-helper', 'testing-helper.c', dependencies : [ @@ -53,6 +53,6 @@ if get_option('tests') libglnx_testlib_dep, ], ) - test(test_name, exe) + test(test_name, exe, depends: testing_helper) endforeach endif From aff1eea9b6fa62a1ef05c8fa0936c69a31a2bf4e Mon Sep 17 00:00:00 2001 From: Andre Klapper Date: Mon, 18 Sep 2023 11:06:20 +0200 Subject: [PATCH 272/280] DOAP: Replace non-existing d-d-l mailing list with GNOME Discourse URL --- libglnx.doap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libglnx.doap b/libglnx.doap index 09e58369..6752e884 100644 --- a/libglnx.doap +++ b/libglnx.doap @@ -20,7 +20,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later - + C From 79b809ae68afbf6fdb69f10fe16c6f94bb87a318 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Tue, 9 Jan 2024 16:45:10 -0500 Subject: [PATCH 273/280] glnx-xattrs: Fix invalid argument to lsetxattr() We weren't passing the right path argument here. This bug would've been quickly noticed if the function were actually used but I still did at least a global GitHub search which didn't return any users. --- glnx-xattrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glnx-xattrs.c b/glnx-xattrs.c index d0e6ab3d..84fd6094 100644 --- a/glnx-xattrs.c +++ b/glnx-xattrs.c @@ -437,7 +437,7 @@ glnx_lsetxattrat (int dfd, char pathbuf[PATH_MAX]; snprintf (pathbuf, sizeof (pathbuf), "/proc/self/fd/%d/%s", dfd, subpath); - if (TEMP_FAILURE_RETRY (lsetxattr (subpath, attribute, value, len, flags)) < 0) + if (TEMP_FAILURE_RETRY (lsetxattr (pathbuf, attribute, value, len, flags)) < 0) return glnx_throw_errno_prefix (error, "lsetxattr(%s)", attribute); return TRUE; From 6ada39c384357e40fb1b89b0293f695e948588eb Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 15:11:24 +0000 Subject: [PATCH 274/280] backports: Add a backport of g_closefrom(), g_fdwalk_set_cloexec() These will be new API in GLib 2.79.2. The only code change in the implementation, other than the _glnx wrappers, was to use `close()` instead of `g_close (fd, NULL)` in a context where it must be async-signal safe. The test-case needed some more adjustments to be backwards-compatible with GLib from the distant past. Signed-off-by: Simon McVittie --- glnx-backports.c | 425 ++++++++++++++++++++++++++++++++- glnx-backports.h | 7 + meson.build | 10 + tests/test-libglnx-backports.c | 196 ++++++++++++++- 4 files changed, 629 insertions(+), 9 deletions(-) diff --git a/glnx-backports.c b/glnx-backports.c index 391c154b..8b6bc4f0 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -1,12 +1,27 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * + * Copyright 2000-2022 Red Hat, Inc. + * Copyright 2006-2007 Matthias Clasen + * Copyright 2006 Padraig O'Briain + * Copyright 2007 Lennart Poettering * Copyright (C) 2015 Colin Walters - * Copyright (C) 2018 Endless OS Foundation, LLC - * SPDX-License-Identifier: LGPL-2.0-or-later + * Copyright 2018-2022 Endless OS Foundation, LLC + * Copyright 2018 Peter Wu + * Copyright 2019 Ting-Wei Lan + * Copyright 2019 Sebastian Schwarz + * Copyright 2020 Matt Rose + * Copyright 2021 Casper Dik + * Copyright 2022 Alexander Richardson + * Copyright 2022 Ray Strode + * Copyright 2022 Thomas Haller + * Copyright 2023-2024 Collabora Ltd. + * Copyright 2023 Sebastian Wilhelmi + * Copyright 2023 CaiJingLong + * SPDX-License-Identifier: LGPL-2.1-or-later * - * This program is free software: you can redistribute it and/or modify + * 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 of the licence or (at + * by the Free Software Foundation; either version 2.1 of the licence or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, @@ -14,16 +29,23 @@ * 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., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . */ #include "libglnx-config.h" #include "glnx-backports.h" +#include +#include +#include +#include +#include +#include +#include +#include + #if !GLIB_CHECK_VERSION(2, 44, 0) gboolean glnx_strv_contains (const gchar * const *strv, @@ -82,3 +104,390 @@ _glnx_strv_equal (const gchar * const *strv1, return (*strv1 == NULL && *strv2 == NULL); } #endif + +#if !GLIB_CHECK_VERSION(2, 80, 0) +/* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ +static int +set_cloexec (void *data, gint fd) +{ + if (fd >= GPOINTER_TO_INT (data)) + fcntl (fd, F_SETFD, FD_CLOEXEC); + + return 0; +} + +/* fdwalk()-compatible callback to close a fd for non-compliant + * implementations of fdwalk() that potentially pass already + * closed fds. + * + * It is not an error to pass an invalid fd to this function. + * + * This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). + */ +G_GNUC_UNUSED static int +close_func_with_invalid_fds (void *data, int fd) +{ + /* We use close and not g_close here because on some platforms, we + * don't know how to close only valid, open file descriptors, so we + * have to pass bad fds to close too. g_close warns if given a bad + * fd. + * + * This function returns no error, because there is nothing that the caller + * could do with that information. That is even the case for EINTR. See + * g_close() about the specialty of EINTR and why that is correct. + * If g_close() ever gets extended to handle EINTR specially, then this place + * should get updated to do the same handling. + */ + if (fd >= GPOINTER_TO_INT (data)) + close (fd); + + return 0; +} + +#ifdef __linux__ +struct linux_dirent64 +{ + guint64 d_ino; /* 64-bit inode number */ + guint64 d_off; /* 64-bit offset to next structure */ + unsigned short d_reclen; /* Size of this dirent */ + unsigned char d_type; /* File type */ + char d_name[]; /* Filename (null-terminated) */ +}; + +/* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ +static gint +filename_to_fd (const char *p) +{ + char c; + int fd = 0; + const int cutoff = G_MAXINT / 10; + const int cutlim = G_MAXINT % 10; + + if (*p == '\0') + return -1; + + while ((c = *p++) != '\0') + { + if (c < '0' || c > '9') + return -1; + c -= '0'; + + /* Check for overflow. */ + if (fd > cutoff || (fd == cutoff && c > cutlim)) + return -1; + + fd = fd * 10 + c; + } + + return fd; +} +#endif + +static int safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data); + +/* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ +static int +safe_fdwalk (int (*cb)(void *data, int fd), void *data) +{ +#if 0 + /* Use fdwalk function provided by the system if it is known to be + * async-signal safe. + * + * Currently there are no operating systems known to provide a safe + * implementation, so this section is not used for now. + */ + return fdwalk (cb, data); +#else + /* Fallback implementation of fdwalk. It should be async-signal safe, but it + * may fail on non-Linux operating systems. See safe_fdwalk_with_invalid_fds + * for a slower alternative. + */ + +#ifdef __linux__ + gint fd; + gint res = 0; + + /* Avoid use of opendir/closedir since these are not async-signal-safe. */ + int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); + if (dir_fd >= 0) + { + /* buf needs to be aligned correctly to receive linux_dirent64. + * C11 has _Alignof for this purpose, but for now a + * union serves the same purpose. */ + union + { + char buf[4096]; + struct linux_dirent64 alignment; + } u; + int pos, nread; + struct linux_dirent64 *de; + + while ((nread = syscall (SYS_getdents64, dir_fd, u.buf, sizeof (u.buf))) > 0) + { + for (pos = 0; pos < nread; pos += de->d_reclen) + { + de = (struct linux_dirent64 *) (u.buf + pos); + + fd = filename_to_fd (de->d_name); + if (fd < 0 || fd == dir_fd) + continue; + + if ((res = cb (data, fd)) != 0) + break; + } + } + + close (dir_fd); + return res; + } + + /* If /proc is not mounted or not accessible we fail here and rely on + * safe_fdwalk_with_invalid_fds to fall back to the old + * rlimit trick. */ + +#endif + +#if defined(__sun__) && defined(F_PREVFD) && defined(F_NEXTFD) +/* + * Solaris 11.4 has a signal-safe way which allows + * us to find all file descriptors in a process. + * + * fcntl(fd, F_NEXTFD, maxfd) + * - returns the first allocated file descriptor <= maxfd > fd. + * + * fcntl(fd, F_PREVFD) + * - return highest allocated file descriptor < fd. + */ + gint fd; + gint res = 0; + + open_max = fcntl (INT_MAX, F_PREVFD); /* find the maximum fd */ + if (open_max < 0) /* No open files */ + return 0; + + for (fd = -1; (fd = fcntl (fd, F_NEXTFD, open_max)) != -1; ) + if ((res = cb (data, fd)) != 0 || fd == open_max) + break; + + return res; +#endif + + return safe_fdwalk_with_invalid_fds (cb, data); +#endif +} + +/* This function is called between fork() and exec() and hence must be + * async-signal-safe (see signal-safety(7)). */ +static int +safe_fdwalk_with_invalid_fds (int (*cb)(void *data, int fd), void *data) +{ + /* Fallback implementation of fdwalk. It should be async-signal safe, but it + * may be slow, especially on systems allowing very high number of open file + * descriptors. + */ + gint open_max = -1; + gint fd; + gint res = 0; + +#if 0 && defined(HAVE_SYS_RESOURCE_H) + struct rlimit rl; + + /* Use getrlimit() function provided by the system if it is known to be + * async-signal safe. + * + * Currently there are no operating systems known to provide a safe + * implementation, so this section is not used for now. + */ + if (getrlimit (RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY) + open_max = rl.rlim_max; +#endif +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + /* Use sysconf() function provided by the system if it is known to be + * async-signal safe. + * + * FreeBSD: sysconf() is included in the list of async-signal safe functions + * found in https://man.freebsd.org/sigaction(2). + * + * OpenBSD: sysconf() is included in the list of async-signal safe functions + * found in https://man.openbsd.org/sigaction.2. + * + * Apple: sysconf() is included in the list of async-signal safe functions + * found in https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man2/sigaction.2 + */ + if (open_max < 0) + open_max = sysconf (_SC_OPEN_MAX); +#endif + /* Hardcoded fallback: the default process hard limit in Linux as of 2020 */ + if (open_max < 0) + open_max = 4096; + +#if defined(__APPLE__) && defined(HAVE_LIBPROC_H) + /* proc_pidinfo isn't documented as async-signal-safe but looking at the implementation + * in the darwin tree here: + * + * https://opensource.apple.com/source/Libc/Libc-498/darwin/libproc.c.auto.html + * + * It's just a thin wrapper around a syscall, so it's probably okay. + */ + { + char buffer[4096 * PROC_PIDLISTFD_SIZE]; + ssize_t buffer_size; + + buffer_size = proc_pidinfo (getpid (), PROC_PIDLISTFDS, 0, buffer, sizeof (buffer)); + + if (buffer_size > 0 && + sizeof (buffer) >= (size_t) buffer_size && + (buffer_size % PROC_PIDLISTFD_SIZE) == 0) + { + const struct proc_fdinfo *fd_info = (const struct proc_fdinfo *) buffer; + size_t number_of_fds = (size_t) buffer_size / PROC_PIDLISTFD_SIZE; + + for (size_t i = 0; i < number_of_fds; i++) + if ((res = cb (data, fd_info[i].proc_fd)) != 0) + break; + + return res; + } + } +#endif + + for (fd = 0; fd < open_max; fd++) + if ((res = cb (data, fd)) != 0) + break; + + return res; +} + +/** + * g_fdwalk_set_cloexec: + * @lowfd: Minimum fd to act on, which must be non-negative + * + * Mark every file descriptor equal to or greater than @lowfd to be closed + * at the next `execve()` or similar, as if via the `FD_CLOEXEC` flag. + * + * Typically @lowfd will be 3, to leave standard input, standard output + * and standard error open after exec. + * + * This is the same as Linux `close_range (lowfd, ~0U, CLOSE_RANGE_CLOEXEC)`, + * but portable to other OSs and to older versions of Linux. + * + * This function is async-signal safe, making it safe to call from a + * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is + * non-negative. + * See [`signal(7)`](man:signal(7)) and + * [`signal-safety(7)`](man:signal-safety(7)) for more details. + * + * Returns: 0 on success, -1 with errno set on error + * Since: 2.80 + */ +int +_glnx_fdwalk_set_cloexec (int lowfd) +{ + int ret; + + g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1)); + +#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC) + /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at + * around the same time. It was designed for use in async-signal-safe + * situations: https://bugs.python.org/issue38061 + * + * The `CLOSE_RANGE_CLOEXEC` flag was added in Linux 5.11, and is not yet + * present in FreeBSD. + * + * Handle ENOSYS in case it’s supported in libc but not the kernel; if so, + * fall back to safe_fdwalk(). Handle EINVAL in case `CLOSE_RANGE_CLOEXEC` + * is not supported. */ + ret = close_range (lowfd, G_MAXUINT, CLOSE_RANGE_CLOEXEC); + if (ret == 0 || !(errno == ENOSYS || errno == EINVAL)) + return ret; +#endif /* HAVE_CLOSE_RANGE */ + + ret = safe_fdwalk (set_cloexec, GINT_TO_POINTER (lowfd)); + + return ret; +} + +/** + * g_closefrom: + * @lowfd: Minimum fd to close, which must be non-negative + * + * Close every file descriptor equal to or greater than @lowfd. + * + * Typically @lowfd will be 3, to leave standard input, standard output + * and standard error open. + * + * This is the same as Linux `close_range (lowfd, ~0U, 0)`, + * but portable to other OSs and to older versions of Linux. + * Equivalently, it is the same as BSD `closefrom (lowfd)`, but portable, + * and async-signal-safe on all OSs. + * + * This function is async-signal safe, making it safe to call from a + * signal handler or a [callback@GLib.SpawnChildSetupFunc], as long as @lowfd is + * non-negative. + * See [`signal(7)`](man:signal(7)) and + * [`signal-safety(7)`](man:signal-safety(7)) for more details. + * + * Returns: 0 on success, -1 with errno set on error + * Since: 2.80 + */ +int +_glnx_closefrom (int lowfd) +{ + int ret; + + g_return_val_if_fail (lowfd >= 0, (errno = EINVAL, -1)); + +#if defined(HAVE_CLOSE_RANGE) + /* close_range() is available in Linux since kernel 5.9, and on FreeBSD at + * around the same time. It was designed for use in async-signal-safe + * situations: https://bugs.python.org/issue38061 + * + * Handle ENOSYS in case it’s supported in libc but not the kernel; if so, + * fall back to safe_fdwalk(). */ + ret = close_range (lowfd, G_MAXUINT, 0); + if (ret == 0 || errno != ENOSYS) + return ret; +#endif /* HAVE_CLOSE_RANGE */ + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || \ + (defined(__sun__) && defined(F_CLOSEFROM)) + /* Use closefrom function provided by the system if it is known to be + * async-signal safe. + * + * FreeBSD: closefrom is included in the list of async-signal safe functions + * found in https://man.freebsd.org/sigaction(2). + * + * OpenBSD: closefrom is not included in the list, but a direct system call + * should be safe to use. + * + * In Solaris as of 11.3 SRU 31, closefrom() is also a direct system call. + * On such systems, F_CLOSEFROM is defined. + */ + (void) closefrom (lowfd); + return 0; +#elif defined(__DragonFly__) + /* It is unclear whether closefrom function included in DragonFlyBSD libc_r + * is safe to use because it calls a lot of library functions. It is also + * unclear whether libc_r itself is still being used. Therefore, we do a + * direct system call here ourselves to avoid possible issues. + */ + (void) syscall (SYS_closefrom, lowfd); + return 0; +#elif defined(F_CLOSEM) + /* NetBSD and AIX have a special fcntl command which does the same thing as + * closefrom. NetBSD also includes closefrom function, which seems to be a + * simple wrapper of the fcntl command. + */ + return fcntl (lowfd, F_CLOSEM); +#else + ret = safe_fdwalk (close_func_with_invalid_fds, GINT_TO_POINTER (lowfd)); + + return ret; +#endif +} +#endif /* !2.80.0 */ diff --git a/glnx-backports.h b/glnx-backports.h index 121e0731..7fa6a920 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -149,4 +149,11 @@ _glnx_steal_fd (int *fdp) #define G_TYPE_FLAG_NONE ((GTypeFlags) 0) #endif +#if !GLIB_CHECK_VERSION(2, 80, 0) +#define g_closefrom _glnx_closefrom +int _glnx_closefrom (int lowfd); +#define g_fdwalk_set_cloexec _glnx_fdwalk_set_cloexec +int _glnx_fdwalk_set_cloexec (int lowfd); +#endif + G_END_DECLS diff --git a/meson.build b/meson.build index a163e994..70c938f9 100644 --- a/meson.build +++ b/meson.build @@ -54,6 +54,16 @@ config_h = configure_file( configuration : conf, ) +check_functions = [ + 'close_range', +] +foreach check_function : check_functions +endforeach +config_h = configure_file( + output : 'libglnx-config.h', + configuration : conf, +) + libglnx_deps = [ dependency('gio-2.0'), dependency('gio-unix-2.0'), diff --git a/tests/test-libglnx-backports.c b/tests/test-libglnx-backports.c index 3683791d..89b1b3f7 100644 --- a/tests/test-libglnx-backports.c +++ b/tests/test-libglnx-backports.c @@ -1,9 +1,10 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 2011 Red Hat, Inc. * Copyright (C) 2018 Endless OS Foundation, LLC * Copyright 2019 Emmanuel Fleury - * Copyright 2021 Collabora Ltd. + * Copyright 2021-2024 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.1-or-later AND LicenseRef-old-glib-tests */ @@ -11,6 +12,193 @@ #include "libglnx.h" #include +#include + +#include +#include + +static void +async_signal_safe_message (const char *message) +{ + if (write (2, message, strlen (message)) < 0 || + write (2, "\n", 1) < 0) + { + /* ignore: not much we can do */ + } +} + +static void test_closefrom_subprocess_einval (void); + +static void +test_closefrom (void) +{ + /* Enough file descriptors to be confident that we're operating on + * all of them */ + const int N_FDS = 20; + int *fds; + int fd; + int i; + pid_t child; + int wait_status; + + /* The loop that populates @fds with pipes assumes this */ + g_assert (N_FDS % 2 == 0); + + for (fd = 0; fd <= 2; fd++) + { + int flags; + + g_assert_no_errno ((flags = fcntl (fd, F_GETFD))); + g_assert_no_errno (fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC)); + } + + fds = g_new0 (int, N_FDS); + + for (i = 0; i < N_FDS; i += 2) + { + GError *error = NULL; + int pipefd[2]; + int res; + + /* Intentionally neither O_CLOEXEC nor FD_CLOEXEC */ + res = g_unix_open_pipe (pipefd, 0, &error); + g_assert (res); + g_assert_no_error (error); + g_clear_error (&error); + fds[i] = pipefd[0]; + fds[i + 1] = pipefd[1]; + } + + child = fork (); + + /* Child process exits with status = 100 + the first wrong fd, + * or 0 if all were correct */ + if (child == 0) + { + for (i = 0; i < N_FDS; i++) + { + int flags = fcntl (fds[i], F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fds[i]); + } + + if (flags & FD_CLOEXEC) + { + async_signal_safe_message ("fd should not have been close-on-exec yet"); + _exit (100 + fds[i]); + } + } + + g_fdwalk_set_cloexec (3); + + for (i = 0; i < N_FDS; i++) + { + int flags = fcntl (fds[i], F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fds[i]); + } + + if (!(flags & FD_CLOEXEC)) + { + async_signal_safe_message ("fd should have been close-on-exec"); + _exit (100 + fds[i]); + } + } + + g_closefrom (3); + + for (fd = 0; fd <= 2; fd++) + { + int flags = fcntl (fd, F_GETFD); + + if (flags == -1) + { + async_signal_safe_message ("fd should not have been closed"); + _exit (100 + fd); + } + + if (flags & FD_CLOEXEC) + { + async_signal_safe_message ("fd should not have been close-on-exec"); + _exit (100 + fd); + } + } + + for (i = 0; i < N_FDS; i++) + { + if (fcntl (fds[i], F_GETFD) != -1 || errno != EBADF) + { + async_signal_safe_message ("fd should have been closed"); + _exit (100 + fds[i]); + } + } + + _exit (0); + } + + g_assert_no_errno (waitpid (child, &wait_status, 0)); + + if (WIFEXITED (wait_status)) + { + int exit_status = WEXITSTATUS (wait_status); + + if (exit_status != 0) + g_test_fail_printf ("File descriptor %d in incorrect state", exit_status - 100); + } + else + { + g_test_fail_printf ("Unexpected wait status %d", wait_status); + } + + for (i = 0; i < N_FDS; i++) + g_assert_no_errno (close (fds[i])); + + g_free (fds); + + if (g_test_undefined ()) + { +#if GLIB_CHECK_VERSION (2, 38, 0) + g_test_trap_subprocess ("/glib-unix/closefrom/subprocess/einval", + 0, G_TEST_SUBPROCESS_DEFAULT); +#else + if (g_test_trap_fork (0, 0)) + { + test_closefrom_subprocess_einval (); + exit (0); + } + +#endif + g_test_trap_assert_passed (); + } +} + +static void +test_closefrom_subprocess_einval (void) +{ + int res; + int errsv; + + g_log_set_always_fatal (G_LOG_FATAL_MASK); + g_log_set_fatal_mask ("GLib", G_LOG_FATAL_MASK); + + errno = 0; + res = g_closefrom (-1); + errsv = errno; + g_assert_cmpint (res, ==, -1); + g_assert_cmpint (errsv, ==, EINVAL); + + errno = 0; + res = g_fdwalk_set_cloexec (-42); + errsv = errno; + g_assert_cmpint (res, ==, -1); + g_assert_cmpint (errsv, ==, EINVAL); +} /* Testing g_memdup2() function with various positive and negative cases */ static void @@ -98,6 +286,12 @@ test_strv_equal (void) int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/glib-unix/closefrom", test_closefrom); +#if GLIB_CHECK_VERSION (2, 38, 0) + g_test_add_func ("/glib-unix/closefrom/subprocess/einval", + test_closefrom_subprocess_einval); +#endif g_test_add_func ("/mainloop/steal-fd", test_steal_fd); g_test_add_func ("/strfuncs/memdup2", test_memdup2); g_test_add_func ("/strfuncs/strv-equal", test_strv_equal); From f2ff19d7c8ec214a0e5d33bde9f86179d871aec2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 16:25:56 +0000 Subject: [PATCH 275/280] build: Really check for close_range() as intended Signed-off-by: Simon McVittie --- libglnx.m4 | 4 ++++ meson.build | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libglnx.m4 b/libglnx.m4 index bd0d580c..e0beac92 100644 --- a/libglnx.m4 +++ b/libglnx.m4 @@ -4,6 +4,7 @@ AC_DEFUN([LIBGLNX_CONFIGURE], [ +dnl This defines HAVE_DECL_FOO to 1 if found or 0 if not AC_CHECK_DECLS([ renameat2, memfd_create, @@ -19,6 +20,9 @@ AC_CHECK_DECLS([ #include #include ]]) +dnl This defines HAVE_FOO to 1 if found, or leaves it undefined if not: +dnl not the same! +AC_CHECK_FUNCS([close_range]) AC_ARG_ENABLE(otmpfile, [AS_HELP_STRING([--disable-otmpfile], diff --git a/meson.build b/meson.build index 70c938f9..c18c7aea 100644 --- a/meson.build +++ b/meson.build @@ -49,16 +49,16 @@ foreach check_function : check_functions ) conf.set10('HAVE_DECL_' + check_function.underscorify().to_upper(), have_it) endforeach -config_h = configure_file( - output : 'libglnx-config.h', - configuration : conf, -) check_functions = [ 'close_range', ] foreach check_function : check_functions + if cc.has_function(check_function) + conf.set('HAVE_' + check_function.underscorify().to_upper(), 1) + endif endforeach + config_h = configure_file( output : 'libglnx-config.h', configuration : conf, From 9d66aa737551ec07eeb85b81fd5dab0b34545da5 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 16:45:31 +0000 Subject: [PATCH 276/280] missing: Add syscall number for close_range() Taken from systemd, but replacing assert_cc with G_STATIC_ASSERT. Signed-off-by: Simon McVittie --- glnx-missing-syscall.h | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index 3ee9dd56..51e28fd7 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -32,6 +32,7 @@ */ #include "libglnx-config.h" +#include #if !HAVE_DECL_RENAMEAT2 # ifndef __NR_renameat2 @@ -155,3 +156,71 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, # define copy_file_range missing_copy_file_range #endif + +#ifndef __IGNORE_close_range +# if defined(__aarch64__) +# define systemd_NR_close_range 436 +# elif defined(__alpha__) +# define systemd_NR_close_range 546 +# elif defined(__arc__) || defined(__tilegx__) +# define systemd_NR_close_range 436 +# elif defined(__arm__) +# define systemd_NR_close_range 436 +# elif defined(__i386__) +# define systemd_NR_close_range 436 +# elif defined(__ia64__) +# define systemd_NR_close_range 1460 +# elif defined(__loongarch_lp64) +# define systemd_NR_close_range 436 +# elif defined(__m68k__) +# define systemd_NR_close_range 436 +# elif defined(_MIPS_SIM) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define systemd_NR_close_range 4436 +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define systemd_NR_close_range 6436 +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define systemd_NR_close_range 5436 +# else +# error "Unknown MIPS ABI" +# endif +# elif defined(__hppa__) +# define systemd_NR_close_range 436 +# elif defined(__powerpc__) +# define systemd_NR_close_range 436 +# elif defined(__riscv) +# if __riscv_xlen == 32 +# define systemd_NR_close_range 436 +# elif __riscv_xlen == 64 +# define systemd_NR_close_range 436 +# else +# error "Unknown RISC-V ABI" +# endif +# elif defined(__s390__) +# define systemd_NR_close_range 436 +# elif defined(__sparc__) +# define systemd_NR_close_range 436 +# elif defined(__x86_64__) +# if defined(__ILP32__) +# define systemd_NR_close_range (436 | /* __X32_SYSCALL_BIT */ 0x40000000) +# else +# define systemd_NR_close_range 436 +# endif +# elif !defined(missing_arch_template) +# warning "close_range() syscall number is unknown for your architecture" +# endif + +/* may be an (invalid) negative number due to libseccomp, see PR 13319 */ +# if defined __NR_close_range && __NR_close_range >= 0 +# if defined systemd_NR_close_range +G_STATIC_ASSERT(__NR_close_range == systemd_NR_close_range); +# endif +# else +# if defined __NR_close_range +# undef __NR_close_range +# endif +# if defined systemd_NR_close_range && systemd_NR_close_range >= 0 +# define __NR_close_range systemd_NR_close_range +# endif +# endif +#endif From debdea29071d60cc9f338f1eaa2de6d84505961c Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 16:46:21 +0000 Subject: [PATCH 277/280] missing: Add a wrapper for close_range() This is not the same as systemd's, because systemd's disagrees with glibc on the signedness of the arguments (https://github.com/systemd/systemd/issues/31270). Signed-off-by: Simon McVittie --- glnx-missing-syscall.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/glnx-missing-syscall.h b/glnx-missing-syscall.h index 51e28fd7..c32a3f73 100644 --- a/glnx-missing-syscall.h +++ b/glnx-missing-syscall.h @@ -224,3 +224,15 @@ G_STATIC_ASSERT(__NR_close_range == systemd_NR_close_range); # endif # endif #endif + +#if !defined(HAVE_CLOSE_RANGE) && defined(__NR_close_range) +static inline int +inline_close_range (unsigned int low, + unsigned int high, + int flags) +{ + return syscall (__NR_close_range, low, high, flags); +} +#define close_range(low, high, flags) inline_close_range(low, high, flags) +#define HAVE_CLOSE_RANGE +#endif From 1e6efc7846db59469c0c569af4b0cc1f34f275ea Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 16:47:50 +0000 Subject: [PATCH 278/280] missing: Add flags for close_range() Signed-off-by: Simon McVittie --- glnx-missing.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/glnx-missing.h b/glnx-missing.h index 5439ae58..fa724b87 100644 --- a/glnx-missing.h +++ b/glnx-missing.h @@ -92,4 +92,12 @@ #define MFD_CLOEXEC 0x0001U #endif +#ifndef CLOSE_RANGE_UNSHARE +#define CLOSE_RANGE_UNSHARE (1U << 1) +#endif + +#ifndef CLOSE_RANGE_CLOEXEC +#define CLOSE_RANGE_CLOEXEC (1U << 2) +#endif + #include "glnx-missing-syscall.h" From e4beedc0f672aa8f5755c295645f4b4ba3eec4ac Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 9 Feb 2024 16:48:47 +0000 Subject: [PATCH 279/280] backports: Use glnx-missing.h to provide missing syscalls Signed-off-by: Simon McVittie --- glnx-backports.c | 1 + 1 file changed, 1 insertion(+) diff --git a/glnx-backports.c b/glnx-backports.c index 8b6bc4f0..d5b68ad0 100644 --- a/glnx-backports.c +++ b/glnx-backports.c @@ -36,6 +36,7 @@ #include "libglnx-config.h" #include "glnx-backports.h" +#include "glnx-missing.h" #include #include From 549d4a35023e1d9c29ccada197595acc39667108 Mon Sep 17 00:00:00 2001 From: bbhtt Date: Mon, 20 Oct 2025 21:23:23 +0530 Subject: [PATCH 280/280] Remove libglnx submodule --- .gitmodules | 3 --- subprojects/libglnx | 1 - 2 files changed, 4 deletions(-) delete mode 160000 subprojects/libglnx diff --git a/.gitmodules b/.gitmodules index 912b635f..8a5c7312 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "libglnx"] - path = subprojects/libglnx - url = https://gitlab.gnome.org/GNOME/libglnx.git [submodule "debugedit"] path = subprojects/debugedit url = https://sourceware.org/git/debugedit.git diff --git a/subprojects/libglnx b/subprojects/libglnx deleted file mode 160000 index 202b294e..00000000 --- a/subprojects/libglnx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 202b294e6079e23242e65e0426f8639841d1210b