├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── COPYING.LIB ├── ChangeLog ├── HACKING ├── Makefile.am ├── NEWS ├── README ├── autogen.sh ├── compat ├── Makefile.am ├── fuse_opt.c └── fuse_opt.h ├── configure.ac └── src ├── Makefile.am ├── afuse-avahissh ├── afuse.c ├── afuse.h ├── dir_list.c ├── dir_list.h ├── fd_list.c ├── fd_list.h ├── string_sorted_list.c ├── string_sorted_list.h ├── utils.c ├── utils.h └── variable_pairing_heap.h /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile* 2 | *.o 3 | aclocal.m4 4 | autom4te.cache/ 5 | compat/.deps/ 6 | config.h 7 | config.h.in 8 | config.log 9 | config.status 10 | configure 11 | depcomp 12 | install-sh 13 | missing 14 | src/.deps/ 15 | src/afuse 16 | stamp-h1 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | before_install: 5 | - sudo apt-get update -qq 6 | - sudo apt-get install -qq libfuse-dev 7 | script: ./autogen.sh && ./configure && make && sudo make install 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Jacob Bower - jacob.bower@gmail.com (Original author) 2 | Pierre Carrier - pierre@gcarrier.fr (Current maintainer) 3 | Henry Gebhardt - hsggebhardt@googlemail.com 4 | Anders Kaseorg 5 | Jeremy Maitin-Shepard 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /COPYING.LIB: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the library GPL. It is 10 | numbered 2 because it goes with version 2 of the ordinary GPL.] 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | Licenses are intended to guarantee your freedom to share and change 17 | free software--to make sure the software is free for all its users. 18 | 19 | This license, the Library General Public License, applies to some 20 | specially designated Free Software Foundation software, and to any 21 | other libraries whose authors decide to use it. You can use it for 22 | your libraries, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if 34 | you distribute copies of the library, or if you modify it. 35 | 36 | For example, if you distribute copies of the library, whether gratis 37 | or for a fee, you must give the recipients all the rights that we gave 38 | you. You must make sure that they, too, receive or can get the source 39 | code. If you link a program with the library, you must provide 40 | complete object files to the recipients so that they can relink them 41 | with the library, after making changes to the library and recompiling 42 | it. And you must show them these terms so they know their rights. 43 | 44 | Our method of protecting your rights has two steps: (1) copyright 45 | the library, and (2) offer you this license which gives you legal 46 | permission to copy, distribute and/or modify the library. 47 | 48 | Also, for each distributor's protection, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | library. If the library is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original 52 | version, so that any problems introduced by others will not reflect on 53 | the original authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that companies distributing free 57 | software will individually obtain patent licenses, thus in effect 58 | transforming the program into proprietary software. To prevent this, 59 | we have made it clear that any patent must be licensed for everyone's 60 | free use or not licensed at all. 61 | 62 | Most GNU software, including some libraries, is covered by the ordinary 63 | GNU General Public License, which was designed for utility programs. This 64 | license, the GNU Library General Public License, applies to certain 65 | designated libraries. This license is quite different from the ordinary 66 | one; be sure to read it in full, and don't assume that anything in it is 67 | the same as in the ordinary license. 68 | 69 | The reason we have a separate public license for some libraries is that 70 | they blur the distinction we usually make between modifying or adding to a 71 | program and simply using it. Linking a program with a library, without 72 | changing the library, is in some sense simply using the library, and is 73 | analogous to running a utility program or application program. However, in 74 | a textual and legal sense, the linked executable is a combined work, a 75 | derivative of the original library, and the ordinary General Public License 76 | treats it as such. 77 | 78 | Because of this blurred distinction, using the ordinary General 79 | Public License for libraries did not effectively promote software 80 | sharing, because most developers did not use the libraries. We 81 | concluded that weaker conditions might promote sharing better. 82 | 83 | However, unrestricted linking of non-free programs would deprive the 84 | users of those programs of all benefit from the free status of the 85 | libraries themselves. This Library General Public License is intended to 86 | permit developers of non-free programs to use free libraries, while 87 | preserving your freedom as a user of such programs to change the free 88 | libraries that are incorporated in them. (We have not seen how to achieve 89 | this as regards changes in header files, but we have achieved it as regards 90 | changes in the actual functions of the Library.) The hope is that this 91 | will lead to faster development of free libraries. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. Pay close attention to the difference between a 95 | "work based on the library" and a "work that uses the library". The 96 | former contains code derived from the library, while the latter only 97 | works together with the library. 98 | 99 | Note that it is possible for a library to be covered by the ordinary 100 | General Public License rather than by this special one. 101 | 102 | GNU LIBRARY GENERAL PUBLIC LICENSE 103 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 104 | 105 | 0. This License Agreement applies to any software library which 106 | contains a notice placed by the copyright holder or other authorized 107 | party saying it may be distributed under the terms of this Library 108 | General Public License (also called "this License"). Each licensee is 109 | addressed as "you". 110 | 111 | A "library" means a collection of software functions and/or data 112 | prepared so as to be conveniently linked with application programs 113 | (which use some of those functions and data) to form executables. 114 | 115 | The "Library", below, refers to any such software library or work 116 | which has been distributed under these terms. A "work based on the 117 | Library" means either the Library or any derivative work under 118 | copyright law: that is to say, a work containing the Library or a 119 | portion of it, either verbatim or with modifications and/or translated 120 | straightforwardly into another language. (Hereinafter, translation is 121 | included without limitation in the term "modification".) 122 | 123 | "Source code" for a work means the preferred form of the work for 124 | making modifications to it. For a library, complete source code means 125 | all the source code for all modules it contains, plus any associated 126 | interface definition files, plus the scripts used to control compilation 127 | and installation of the library. 128 | 129 | Activities other than copying, distribution and modification are not 130 | covered by this License; they are outside its scope. The act of 131 | running a program using the Library is not restricted, and output from 132 | such a program is covered only if its contents constitute a work based 133 | on the Library (independent of the use of the Library in a tool for 134 | writing it). Whether that is true depends on what the Library does 135 | and what the program that uses the Library does. 136 | 137 | 1. You may copy and distribute verbatim copies of the Library's 138 | complete source code as you receive it, in any medium, provided that 139 | you conspicuously and appropriately publish on each copy an 140 | appropriate copyright notice and disclaimer of warranty; keep intact 141 | all the notices that refer to this License and to the absence of any 142 | warranty; and distribute a copy of this License along with the 143 | Library. 144 | 145 | You may charge a fee for the physical act of transferring a copy, 146 | and you may at your option offer warranty protection in exchange for a 147 | fee. 148 | 149 | 2. You may modify your copy or copies of the Library or any portion 150 | of it, thus forming a work based on the Library, and copy and 151 | distribute such modifications or work under the terms of Section 1 152 | above, provided that you also meet all of these conditions: 153 | 154 | a) The modified work must itself be a software library. 155 | 156 | b) You must cause the files modified to carry prominent notices 157 | stating that you changed the files and the date of any change. 158 | 159 | c) You must cause the whole of the work to be licensed at no 160 | charge to all third parties under the terms of this License. 161 | 162 | d) If a facility in the modified Library refers to a function or a 163 | table of data to be supplied by an application program that uses 164 | the facility, other than as an argument passed when the facility 165 | is invoked, then you must make a good faith effort to ensure that, 166 | in the event an application does not supply such function or 167 | table, the facility still operates, and performs whatever part of 168 | its purpose remains meaningful. 169 | 170 | (For example, a function in a library to compute square roots has 171 | a purpose that is entirely well-defined independent of the 172 | application. Therefore, Subsection 2d requires that any 173 | application-supplied function or table used by this function must 174 | be optional: if the application does not supply it, the square 175 | root function must still compute square roots.) 176 | 177 | These requirements apply to the modified work as a whole. If 178 | identifiable sections of that work are not derived from the Library, 179 | and can be reasonably considered independent and separate works in 180 | themselves, then this License, and its terms, do not apply to those 181 | sections when you distribute them as separate works. But when you 182 | distribute the same sections as part of a whole which is a work based 183 | on the Library, the distribution of the whole must be on the terms of 184 | this License, whose permissions for other licensees extend to the 185 | entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest 189 | your rights to work written entirely by you; rather, the intent is to 190 | exercise the right to control the distribution of derivative or 191 | collective works based on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library 194 | with the Library (or with a work based on the Library) on a volume of 195 | a storage or distribution medium does not bring the other work under 196 | the scope of this License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public 199 | License instead of this License to a given copy of the Library. To do 200 | this, you must alter all the notices that refer to this License, so 201 | that they refer to the ordinary GNU General Public License, version 2, 202 | instead of to this License. (If a newer version than version 2 of the 203 | ordinary GNU General Public License has appeared, then you can specify 204 | that version instead if you wish.) Do not make any other change in 205 | these notices. 206 | 207 | Once this change is made in a given copy, it is irreversible for 208 | that copy, so the ordinary GNU General Public License applies to all 209 | subsequent copies and derivative works made from that copy. 210 | 211 | This option is useful when you wish to copy part of the code of 212 | the Library into a program that is not a library. 213 | 214 | 4. You may copy and distribute the Library (or a portion or 215 | derivative of it, under Section 2) in object code or executable form 216 | under the terms of Sections 1 and 2 above provided that you accompany 217 | it with the complete corresponding machine-readable source code, which 218 | must be distributed under the terms of Sections 1 and 2 above on a 219 | medium customarily used for software interchange. 220 | 221 | If distribution of object code is made by offering access to copy 222 | from a designated place, then offering equivalent access to copy the 223 | source code from the same place satisfies the requirement to 224 | distribute the source code, even though third parties are not 225 | compelled to copy the source along with the object code. 226 | 227 | 5. A program that contains no derivative of any portion of the 228 | Library, but is designed to work with the Library by being compiled or 229 | linked with it, is called a "work that uses the Library". Such a 230 | work, in isolation, is not a derivative work of the Library, and 231 | therefore falls outside the scope of this License. 232 | 233 | However, linking a "work that uses the Library" with the Library 234 | creates an executable that is a derivative of the Library (because it 235 | contains portions of the Library), rather than a "work that uses the 236 | library". The executable is therefore covered by this License. 237 | Section 6 states terms for distribution of such executables. 238 | 239 | When a "work that uses the Library" uses material from a header file 240 | that is part of the Library, the object code for the work may be a 241 | derivative work of the Library even though the source code is not. 242 | Whether this is true is especially significant if the work can be 243 | linked without the Library, or if the work is itself a library. The 244 | threshold for this to be true is not precisely defined by law. 245 | 246 | If such an object file uses only numerical parameters, data 247 | structure layouts and accessors, and small macros and small inline 248 | functions (ten lines or less in length), then the use of the object 249 | file is unrestricted, regardless of whether it is legally a derivative 250 | work. (Executables containing this object code plus portions of the 251 | Library will still fall under Section 6.) 252 | 253 | Otherwise, if the work is a derivative of the Library, you may 254 | distribute the object code for the work under the terms of Section 6. 255 | Any executables containing that work also fall under Section 6, 256 | whether or not they are linked directly with the Library itself. 257 | 258 | 6. As an exception to the Sections above, you may also compile or 259 | link a "work that uses the Library" with the Library to produce a 260 | work containing portions of the Library, and distribute that work 261 | under terms of your choice, provided that the terms permit 262 | modification of the work for the customer's own use and reverse 263 | engineering for debugging such modifications. 264 | 265 | You must give prominent notice with each copy of the work that the 266 | Library is used in it and that the Library and its use are covered by 267 | this License. You must supply a copy of this License. If the work 268 | during execution displays copyright notices, you must include the 269 | copyright notice for the Library among them, as well as a reference 270 | directing the user to the copy of this License. Also, you must do one 271 | of these things: 272 | 273 | a) Accompany the work with the complete corresponding 274 | machine-readable source code for the Library including whatever 275 | changes were used in the work (which must be distributed under 276 | Sections 1 and 2 above); and, if the work is an executable linked 277 | with the Library, with the complete machine-readable "work that 278 | uses the Library", as object code and/or source code, so that the 279 | user can modify the Library and then relink to produce a modified 280 | executable containing the modified Library. (It is understood 281 | that the user who changes the contents of definitions files in the 282 | Library will not necessarily be able to recompile the application 283 | to use the modified definitions.) 284 | 285 | b) Accompany the work with a written offer, valid for at 286 | least three years, to give the same user the materials 287 | specified in Subsection 6a, above, for a charge no more 288 | than the cost of performing this distribution. 289 | 290 | c) If distribution of the work is made by offering access to copy 291 | from a designated place, offer equivalent access to copy the above 292 | specified materials from the same place. 293 | 294 | d) Verify that the user has already received a copy of these 295 | materials or that you have already sent this user a copy. 296 | 297 | For an executable, the required form of the "work that uses the 298 | Library" must include any data and utility programs needed for 299 | reproducing the executable from it. However, as a special exception, 300 | the source code distributed need not include anything that is normally 301 | distributed (in either source or binary form) with the major 302 | components (compiler, kernel, and so on) of the operating system on 303 | which the executable runs, unless that component itself accompanies 304 | the executable. 305 | 306 | It may happen that this requirement contradicts the license 307 | restrictions of other proprietary libraries that do not normally 308 | accompany the operating system. Such a contradiction means you cannot 309 | use both them and the Library together in an executable that you 310 | distribute. 311 | 312 | 7. You may place library facilities that are a work based on the 313 | Library side-by-side in a single library together with other library 314 | facilities not covered by this License, and distribute such a combined 315 | library, provided that the separate distribution of the work based on 316 | the Library and of the other library facilities is otherwise 317 | permitted, and provided that you do these two things: 318 | 319 | a) Accompany the combined library with a copy of the same work 320 | based on the Library, uncombined with any other library 321 | facilities. This must be distributed under the terms of the 322 | Sections above. 323 | 324 | b) Give prominent notice with the combined library of the fact 325 | that part of it is a work based on the Library, and explaining 326 | where to find the accompanying uncombined form of the same work. 327 | 328 | 8. You may not copy, modify, sublicense, link with, or distribute 329 | the Library except as expressly provided under this License. Any 330 | attempt otherwise to copy, modify, sublicense, link with, or 331 | distribute the Library is void, and will automatically terminate your 332 | rights under this License. However, parties who have received copies, 333 | or rights, from you under this License will not have their licenses 334 | terminated so long as such parties remain in full compliance. 335 | 336 | 9. You are not required to accept this License, since you have not 337 | signed it. However, nothing else grants you permission to modify or 338 | distribute the Library or its derivative works. These actions are 339 | prohibited by law if you do not accept this License. Therefore, by 340 | modifying or distributing the Library (or any work based on the 341 | Library), you indicate your acceptance of this License to do so, and 342 | all its terms and conditions for copying, distributing or modifying 343 | the Library or works based on it. 344 | 345 | 10. Each time you redistribute the Library (or any work based on the 346 | Library), the recipient automatically receives a license from the 347 | original licensor to copy, distribute, link with or modify the Library 348 | subject to these terms and conditions. You may not impose any further 349 | restrictions on the recipients' exercise of the rights granted herein. 350 | You are not responsible for enforcing compliance by third parties to 351 | this License. 352 | 353 | 11. If, as a consequence of a court judgment or allegation of patent 354 | infringement or for any other reason (not limited to patent issues), 355 | conditions are imposed on you (whether by court order, agreement or 356 | otherwise) that contradict the conditions of this License, they do not 357 | excuse you from the conditions of this License. If you cannot 358 | distribute so as to satisfy simultaneously your obligations under this 359 | License and any other pertinent obligations, then as a consequence you 360 | may not distribute the Library at all. For example, if a patent 361 | license would not permit royalty-free redistribution of the Library by 362 | all those who receive copies directly or indirectly through you, then 363 | the only way you could satisfy both it and this License would be to 364 | refrain entirely from distribution of the Library. 365 | 366 | If any portion of this section is held invalid or unenforceable under any 367 | particular circumstance, the balance of the section is intended to apply, 368 | and the section as a whole is intended to apply in other circumstances. 369 | 370 | It is not the purpose of this section to induce you to infringe any 371 | patents or other property right claims or to contest validity of any 372 | such claims; this section has the sole purpose of protecting the 373 | integrity of the free software distribution system which is 374 | implemented by public license practices. Many people have made 375 | generous contributions to the wide range of software distributed 376 | through that system in reliance on consistent application of that 377 | system; it is up to the author/donor to decide if he or she is willing 378 | to distribute software through any other system and a licensee cannot 379 | impose that choice. 380 | 381 | This section is intended to make thoroughly clear what is believed to 382 | be a consequence of the rest of this License. 383 | 384 | 12. If the distribution and/or use of the Library is restricted in 385 | certain countries either by patents or by copyrighted interfaces, the 386 | original copyright holder who places the Library under this License may add 387 | an explicit geographical distribution limitation excluding those countries, 388 | so that distribution is permitted only in or among countries not thus 389 | excluded. In such case, this License incorporates the limitation as if 390 | written in the body of this License. 391 | 392 | 13. The Free Software Foundation may publish revised and/or new 393 | versions of the Library General Public License from time to time. 394 | Such new versions will be similar in spirit to the present version, 395 | but may differ in detail to address new problems or concerns. 396 | 397 | Each version is given a distinguishing version number. If the Library 398 | specifies a version number of this License which applies to it and 399 | "any later version", you have the option of following the terms and 400 | conditions either of that version or of any later version published by 401 | the Free Software Foundation. If the Library does not specify a 402 | license version number, you may choose any version ever published by 403 | the Free Software Foundation. 404 | 405 | 14. If you wish to incorporate parts of the Library into other free 406 | programs whose distribution conditions are incompatible with these, 407 | write to the author to ask for permission. For software which is 408 | copyrighted by the Free Software Foundation, write to the Free 409 | Software Foundation; we sometimes make exceptions for this. Our 410 | decision will be guided by the two goals of preserving the free status 411 | of all derivatives of our free software and of promoting the sharing 412 | and reuse of software generally. 413 | 414 | NO WARRANTY 415 | 416 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 417 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 418 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 419 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 420 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 421 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 422 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 423 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 424 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 425 | 426 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 427 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 428 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 429 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 430 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 431 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 432 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 433 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 434 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 435 | DAMAGES. 436 | 437 | END OF TERMS AND CONDITIONS 438 | 439 | Appendix: How to Apply These Terms to Your New Libraries 440 | 441 | If you develop a new library, and you want it to be of the greatest 442 | possible use to the public, we recommend making it free software that 443 | everyone can redistribute and change. You can do so by permitting 444 | redistribution under these terms (or, alternatively, under the terms of the 445 | ordinary General Public License). 446 | 447 | To apply these terms, attach the following notices to the library. It is 448 | safest to attach them to the start of each source file to most effectively 449 | convey the exclusion of warranty; and each file should have at least the 450 | "copyright" line and a pointer to where the full notice is found. 451 | 452 | 453 | Copyright (C) 454 | 455 | This library is free software; you can redistribute it and/or 456 | modify it under the terms of the GNU Library General Public 457 | License as published by the Free Software Foundation; either 458 | version 2 of the License, or (at your option) any later version. 459 | 460 | This library is distributed in the hope that it will be useful, 461 | but WITHOUT ANY WARRANTY; without even the implied warranty of 462 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 463 | Library General Public License for more details. 464 | 465 | You should have received a copy of the GNU Library General Public 466 | License along with this library; if not, write to the Free 467 | Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 468 | MA 02111-1307, USA 469 | 470 | Also add information on how to contact you by electronic and paper mail. 471 | 472 | You should also get your employer (if you work as a programmer) or your 473 | school, if any, to sign a "copyright disclaimer" for the library, if 474 | necessary. Here is a sample; alter the names: 475 | 476 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 477 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 478 | 479 | , 1 April 1990 480 | Ty Coon, President of Vice 481 | 482 | That's all there is to it! 483 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcarrier/afuse/30057222cc58ffe1f8c0889fce8da314d5364691/ChangeLog -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | Development build 2 | ----------------- 3 | If you get afuse from git, run ./autogen.sh to generate various autotools 4 | scripts and config. 5 | 6 | Contributing 7 | ------------ 8 | 9 | Help is always welcome. Please feel free to jump in, open issues or submit 10 | pull requests. 11 | 12 | For consistency purposes, please reformat your code using GNU indent: 13 | $ indent --linux-style *.h *.c 14 | 15 | TODO 16 | ---- 17 | While afuse is basically usable, there are a huge number of things which need 18 | doing to afuse to make it "good". Here are a list of some of these: 19 | 20 | * Support multi-threading - Internal Data structures need to guarded 21 | before we allow this. 22 | 23 | * Eliminate the proxying - In theory it should be possible for afuse 24 | to mount filesystems within itself. However this seems to cause a 25 | really nasty deadlock. It might be possible for this to work using 26 | multi-threading or rebinding/moving mount points (needs changes to 27 | fusermount). 28 | 29 | * Rather than umounting an FS to ensure directory changes are flushed, 30 | it should be possible to flush a handle to the parent dir instead. 31 | 32 | * More complex automounting schemes 33 | * Different commands based on regex's of virtual directories. 34 | * Scriptable multi-level virtual directory hierarchy. 35 | 36 | * Refactoring 37 | * Code should be split into multiple files (could be more efficient too). 38 | * Some internal terminology can be confusing. 39 | * alloca's should be converted to VLAs as alloca is non-portable. 40 | * Code should in general be made more portable with the help of the 41 | autotools. 42 | 43 | * More extensive documentation. 44 | 45 | * GUI for interactive automounting. For example we could pop up 46 | an X11 window, or switch to other VTs on a Linux console. 47 | This needs some thought to be implemented well, maybe it should be done with 48 | plug-ins/scripts on a per-FS basis. 49 | 50 | * Better debugging output. 51 | 52 | * Autotool'ed environment needs lots of work. 53 | 54 | * Exit on signal often isn't clean (directory should be unmounted) 55 | (actually this seems to work with FUSE 2.3?) 56 | 57 | * Mounting can sometimes happen in silly situations, for example touching 58 | a file in the afuse root will cause a filesystem of that filename 59 | to be automounted. This does not seem to be avoidable in earlier 60 | versions of FUSE (2.3 for example) as getattr is always called on 61 | the virtual directory on any operation and if this fails the whole 62 | operation is aborted. So it is impossible to distinguish a getattr for 63 | [non]-mounting operations. 64 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | if FUSE_OPT_COMPAT 2 | compat_dir = compat 3 | endif 4 | 5 | SUBDIRS=$(compat_dir) src 6 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Version 0.5.0 2 | ------------- 3 | 4 | * Adds the -o mount_dir=DIR option, which allows users to specify a directory 5 | under which all temporary mounts are made. 6 | 7 | Version 0.4.1 8 | ------------- 9 | * Fix bug where afuse crashes on release 10 | 11 | Version 0.4 12 | ----------- 13 | * Mac OS X 10.6+ and FreeBSD are supported. 14 | * Added ability to filter paths from auto-mounting via a file of patterns. 15 | * The path 'autorun.inf' is hard-coded as a filter to fix a problem in 16 | Ubuntu/GNOME 3 desktop environments where a lookup occurs as soon as any afuse 17 | mount is made. 18 | * Ability to populate root directory using output from an external process. 19 | * Added 'exact_getattr' option. 20 | 21 | Version 0.2 22 | ----------- 23 | * New features: 24 | + Timeout-based unmounting. 25 | + Forced immediate unmounting by removing root directory of 26 | auto mounts. 27 | * Fixes/optimisations: 28 | + Minor performance tweaks. 29 | + Better handling of filesystems which are unexpectedly 30 | unmounted. 31 | 32 | Version 0.1.1 33 | ------------- 34 | * Backward compatibility with FUSE 2.3 (to 2.5). 35 | * Internal bug fixes. 36 | * Moved to SVN due to SourceForge CVS being perpetually broken. 37 | 38 | Version 0.1 39 | ----------- 40 | * Initial release 41 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | afuse 0.5.0 2 | https://github.com/pcarrier/afuse/ 3 | 4 | 5 | 0. Contents 6 | ----------- 7 | 1. Abstract 8 | 2. Basic Usage 9 | 3. Pre-populating afuse Root 10 | 4. Misc Other Features 11 | 5. Important Notes on afuse's Operation 12 | 13 | 14 | 1. Abstract 15 | ----------- 16 | afuse is an automounting file system implemented in user-space using FUSE. 17 | afuse currently implements the most basic functionality that can be expected by 18 | an automounter; that is it manages a directory of virtual directories. If one 19 | of these virtual directories is accessed and is not already automounted, afuse 20 | will attempt to mount a filesystem onto that directory. If the mount succeeds 21 | the requested access proceeds as normal, otherwise it will fail with an error. 22 | See the example below for a specific usage scenario. 23 | 24 | The advantage of using afuse over traditional automounters is afuse runs 25 | entirely in user-space by individual users. Thus it can take advantage of the 26 | invoking users environment, for example allowing access to an ssh-agent for 27 | password-less sshfs mounts, or allowing access to a graphical environment to 28 | get user input to complete a mount such as asking for a password. 29 | 30 | afuse is distributed under the GPLv2 license, details of which can be found in 31 | the COPYING file. Particularly, please note that while afuse is intended to be 32 | useful it is provided with ABSOLUTELY NO WARRANTY. 33 | 34 | If you are interested in contributing to afuse, please read the HACKING file. 35 | 36 | 37 | 2. Basic Usage 38 | -------------- 39 | Example invocation using sshfs: 40 | 41 | afuse -o mount_template="sshfs %r:/ %m" \ 42 | -o unmount_template="fusermount -u -z %m" \ 43 | mountpoint/ 44 | 45 | Now try 'ls mountpoint/user@host/'. 46 | 47 | To unmount use: 48 | 49 | fusermount -u -z mountpoint/ 50 | 51 | All sub mounts should be automatically unmounted. 52 | 53 | For this example to work, the sshfs invocation must not require user 54 | interactivity (i.e. asking for a password). So you probably want to be 55 | using something like ssh-agent. 56 | 57 | Alternatively, if want interactivity, add -f to the afuse invocation. 58 | 59 | 60 | 3. Pre-populating afuse Root 61 | ---------------------------- 62 | By default the afuse root directory is empty until a specific access is made to 63 | a directory leading to successful mount. In some cases it may be useful to 64 | pre-populate the afuse root with valid directory names. 65 | 66 | To enable root directory pre-population, use the -o populate_root_command 67 | option to specify a command which will provide a list of directory names to 68 | populate the root with. This program will be run for every directory list 69 | request on the afuse root directory, and should output one dir entry per-line 70 | to stdout. As this program is called repeatedly and for every directory 71 | listing it should not block/pause for an unreasonable amount of time. 72 | 73 | Note that when using this mode no actual mounting occurs until a directory 74 | access is made to one of the potential mount-points. 75 | 76 | An example of this usage is provided in the afuse-avahissh shell script 77 | (typically installed alongside afuse). This can be started as follows: 78 | 79 | afuse-avahissh mountpoint/ 80 | 81 | This script will use avahi to automatically populate the root directory with 82 | hosts advertising their sftp service via Avahi. To work correctly both sshfs 83 | and avahi-browse must be installed and available to the script. 84 | 85 | 86 | 4. Misc Other Features 87 | ---------------------- 88 | * By default afuse does not attempt to mount a directory on a getattr 89 | operation. This can be disabled using the -o exact_getattr option. This 90 | allows getattr to return accurate information but may cause spurious mounts 91 | when programs are just checking for the existence of files. 92 | 93 | * The -o flushwrites option causes write operation on file-systems mounted by 94 | afuse to operate synchronously. 95 | 96 | 97 | 5. Important Notes on afuse's Operation 98 | --------------------------------------- 99 | One of the most important things to note about afuse's operation is that 100 | automounted filesystems accessed through afuse are actually accessed by proxy. 101 | Actual mounts are created in an instance specific directory in /tmp. _ALL_ 102 | accesses to automounted filesystems apparently managed by afuse go through 103 | afuse and are proxied onto the real filesystem mounts as appropriate. 104 | 105 | While this shouldn't cause any operational problems, it does mean that 106 | operations on afuse automounted filesystems have considerable overhead. It can 107 | also mean that if afuse is not shutdown cleanly (via an unmount of the afuse 108 | filesystem) a stale directory can be left in /tmp of the form afuse-XXXXXX 109 | (where the X's are random characters). 110 | 111 | Hopefully these limitations will be removed in later revisions of afuse. 112 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | autoreconf --install --verbose 5 | ./configure "$@" 6 | -------------------------------------------------------------------------------- /compat/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_LIBRARIES = libcompat.a 2 | libcompat_a_SOURCES = fuse_opt.c fuse_opt.h 3 | -------------------------------------------------------------------------------- /compat/fuse_opt.c: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2006 Miklos Szeredi 4 | 5 | This file is borrowed from sshfs-fuse. It provides an implementation 6 | of the FUSE command line parsing functions which only appear in 7 | later version of FUSE but are used in afuse. 8 | 9 | This file can be distributed under the terms of the GNU LGPL. 10 | See the file COPYING.LIB 11 | */ 12 | 13 | #include "fuse_opt.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | struct fuse_opt_context { 21 | void *data; 22 | const struct fuse_opt *opt; 23 | fuse_opt_proc_t proc; 24 | int argctr; 25 | int argc; 26 | char **argv; 27 | struct fuse_args outargs; 28 | char *opts; 29 | int nonopt; 30 | }; 31 | 32 | void fuse_opt_free_args(struct fuse_args *args) 33 | { 34 | if (args && args->argv && args->allocated) { 35 | int i; 36 | for (i = 0; i < args->argc; i++) 37 | free(args->argv[i]); 38 | free(args->argv); 39 | args->argv = NULL; 40 | args->allocated = 0; 41 | } 42 | } 43 | 44 | static int alloc_failed(void) 45 | { 46 | fprintf(stderr, "fuse: memory allocation failed\n"); 47 | return -1; 48 | } 49 | 50 | int fuse_opt_add_arg(struct fuse_args *args, const char *arg) 51 | { 52 | char **newargv; 53 | char *newarg; 54 | 55 | assert(!args->argv || args->allocated); 56 | 57 | newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); 58 | newarg = newargv ? strdup(arg) : NULL; 59 | if (!newargv || !newarg) 60 | return alloc_failed(); 61 | 62 | args->argv = newargv; 63 | args->allocated = 1; 64 | args->argv[args->argc++] = newarg; 65 | args->argv[args->argc] = NULL; 66 | return 0; 67 | } 68 | 69 | static int next_arg(struct fuse_opt_context *ctx, const char *opt) 70 | { 71 | if (ctx->argctr + 1 >= ctx->argc) { 72 | fprintf(stderr, "fuse: missing argument after `%s'\n", opt); 73 | return -1; 74 | } 75 | ctx->argctr++; 76 | return 0; 77 | } 78 | 79 | static int add_arg(struct fuse_opt_context *ctx, const char *arg) 80 | { 81 | return fuse_opt_add_arg(&ctx->outargs, arg); 82 | } 83 | 84 | int fuse_opt_add_opt(char **opts, const char *opt) 85 | { 86 | char *newopts; 87 | if (!*opts) 88 | newopts = strdup(opt); 89 | else { 90 | unsigned oldlen = strlen(*opts); 91 | newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1); 92 | if (newopts) { 93 | newopts[oldlen] = ','; 94 | strcpy(newopts + oldlen + 1, opt); 95 | } 96 | } 97 | if (!newopts) 98 | return alloc_failed(); 99 | 100 | *opts = newopts; 101 | return 0; 102 | } 103 | 104 | static int add_opt(struct fuse_opt_context *ctx, const char *opt) 105 | { 106 | return fuse_opt_add_opt(&ctx->opts, opt); 107 | } 108 | 109 | static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg) 110 | { 111 | assert(pos <= ctx->outargs.argc); 112 | if (add_arg(ctx, arg) == -1) 113 | return -1; 114 | 115 | if (pos != ctx->outargs.argc - 1) { 116 | char *newarg = ctx->outargs.argv[ctx->outargs.argc - 1]; 117 | memmove(&ctx->outargs.argv[pos + 1], &ctx->outargs.argv[pos], 118 | sizeof(char *) * (ctx->outargs.argc - pos - 1)); 119 | ctx->outargs.argv[pos] = newarg; 120 | } 121 | return 0; 122 | } 123 | 124 | static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, 125 | int iso) 126 | { 127 | if (ctx->proc) { 128 | int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); 129 | if (res == -1 || !res) 130 | return res; 131 | } 132 | if (iso) 133 | return add_opt(ctx, arg); 134 | else 135 | return add_arg(ctx, arg); 136 | } 137 | 138 | static int match_template(const char *t, const char *arg, unsigned *sepp) 139 | { 140 | int arglen = strlen(arg); 141 | const char *sep = strchr(t, '='); 142 | sep = sep ? sep : strchr(t, ' '); 143 | if (sep && (!sep[1] || sep[1] == '%')) { 144 | int tlen = sep - t; 145 | if (sep[0] == '=') 146 | tlen++; 147 | if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { 148 | *sepp = sep - t; 149 | return 1; 150 | } 151 | } 152 | if (strcmp(t, arg) == 0) { 153 | *sepp = 0; 154 | return 1; 155 | } 156 | return 0; 157 | } 158 | 159 | static const struct fuse_opt *find_opt(const struct fuse_opt *opt, 160 | const char *arg, unsigned *sepp) 161 | { 162 | for (; opt && opt->template; opt++) 163 | if (match_template(opt->template, arg, sepp)) 164 | return opt; 165 | return NULL; 166 | } 167 | 168 | int fuse_opt_match(const struct fuse_opt *opts, const char *opt) 169 | { 170 | unsigned dummy; 171 | return find_opt(opts, opt, &dummy) ? 1 : 0; 172 | } 173 | 174 | static int process_opt_param(void *var, const char *format, const char *param, 175 | const char *arg) 176 | { 177 | assert(format[0] == '%'); 178 | if (format[1] == 's') { 179 | char *copy = strdup(param); 180 | if (!copy) 181 | return alloc_failed(); 182 | 183 | *(char **)var = copy; 184 | } else { 185 | if (sscanf(param, format, var) != 1) { 186 | fprintf(stderr, 187 | "fuse: invalid parameter in option `%s'\n", 188 | arg); 189 | return -1; 190 | } 191 | } 192 | return 0; 193 | } 194 | 195 | static int process_opt(struct fuse_opt_context *ctx, 196 | const struct fuse_opt *opt, unsigned sep, 197 | const char *arg, int iso) 198 | { 199 | if (opt->offset == -1U) { 200 | if (call_proc(ctx, arg, opt->value, iso) == -1) 201 | return -1; 202 | } else { 203 | void *var = ctx->data + opt->offset; 204 | if (sep && opt->template[sep + 1]) { 205 | const char *param = arg + sep; 206 | if (opt->template[sep] == '=') 207 | param++; 208 | if (process_opt_param(var, opt->template + sep + 1, 209 | param, arg) == -1) 210 | return -1; 211 | } else 212 | *(int *)var = opt->value; 213 | } 214 | return 0; 215 | } 216 | 217 | static int process_opt_sep_arg(struct fuse_opt_context *ctx, 218 | const struct fuse_opt *opt, unsigned sep, 219 | const char *arg, int iso) 220 | { 221 | int res; 222 | char *newarg; 223 | char *param; 224 | 225 | if (next_arg(ctx, arg) == -1) 226 | return -1; 227 | 228 | param = ctx->argv[ctx->argctr]; 229 | newarg = malloc(sep + strlen(param) + 1); 230 | if (!newarg) 231 | return alloc_failed(); 232 | 233 | memcpy(newarg, arg, sep); 234 | strcpy(newarg + sep, param); 235 | res = process_opt(ctx, opt, sep, newarg, iso); 236 | free(newarg); 237 | 238 | return res; 239 | } 240 | 241 | static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) 242 | { 243 | unsigned sep; 244 | const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); 245 | if (opt) { 246 | for (; opt; opt = find_opt(opt + 1, arg, &sep)) { 247 | int res; 248 | if (sep && opt->template[sep] == ' ' && !arg[sep]) 249 | res = 250 | process_opt_sep_arg(ctx, opt, sep, arg, 251 | iso); 252 | else 253 | res = process_opt(ctx, opt, sep, arg, iso); 254 | if (res == -1) 255 | return -1; 256 | } 257 | return 0; 258 | } else 259 | return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); 260 | } 261 | 262 | static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) 263 | { 264 | char *sep; 265 | 266 | do { 267 | int res; 268 | sep = strchr(opts, ','); 269 | if (sep) 270 | *sep = '\0'; 271 | res = process_gopt(ctx, opts, 1); 272 | if (res == -1) 273 | return -1; 274 | opts = sep + 1; 275 | } while (sep); 276 | 277 | return 0; 278 | } 279 | 280 | static int process_option_group(struct fuse_opt_context *ctx, const char *opts) 281 | { 282 | int res; 283 | char *copy; 284 | const char *sep = strchr(opts, ','); 285 | if (!sep) 286 | return process_gopt(ctx, opts, 1); 287 | 288 | copy = strdup(opts); 289 | if (!copy) { 290 | fprintf(stderr, "fuse: memory allocation failed\n"); 291 | return -1; 292 | } 293 | res = process_real_option_group(ctx, copy); 294 | free(copy); 295 | return res; 296 | } 297 | 298 | static int process_one(struct fuse_opt_context *ctx, const char *arg) 299 | { 300 | if (ctx->nonopt || arg[0] != '-') 301 | return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); 302 | else if (arg[1] == 'o') { 303 | if (arg[2]) 304 | return process_option_group(ctx, arg + 2); 305 | else { 306 | if (next_arg(ctx, arg) == -1) 307 | return -1; 308 | 309 | return process_option_group(ctx, 310 | ctx->argv[ctx->argctr]); 311 | } 312 | } else if (arg[1] == '-' && !arg[2]) { 313 | if (add_arg(ctx, arg) == -1) 314 | return -1; 315 | ctx->nonopt = ctx->outargs.argc; 316 | return 0; 317 | } else 318 | return process_gopt(ctx, arg, 0); 319 | } 320 | 321 | static int opt_parse(struct fuse_opt_context *ctx) 322 | { 323 | if (ctx->argc) { 324 | if (add_arg(ctx, ctx->argv[0]) == -1) 325 | return -1; 326 | } 327 | 328 | for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) 329 | if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) 330 | return -1; 331 | 332 | if (ctx->opts) { 333 | if (insert_arg(ctx, 1, "-o") == -1 || 334 | insert_arg(ctx, 2, ctx->opts) == -1) 335 | return -1; 336 | } 337 | if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) 338 | ctx->outargs.argv[--ctx->outargs.argc] = NULL; 339 | 340 | return 0; 341 | } 342 | 343 | int fuse_opt_parse(struct fuse_args *args, void *data, 344 | const struct fuse_opt opts[], fuse_opt_proc_t proc) 345 | { 346 | int res; 347 | struct fuse_opt_context ctx = { 348 | .data = data, 349 | .opt = opts, 350 | .proc = proc, 351 | }; 352 | 353 | if (!args || !args->argv || !args->argc) 354 | return 0; 355 | 356 | ctx.argc = args->argc; 357 | ctx.argv = args->argv; 358 | 359 | res = opt_parse(&ctx); 360 | if (res != -1) { 361 | struct fuse_args tmp = *args; 362 | *args = ctx.outargs; 363 | ctx.outargs = tmp; 364 | } 365 | free(ctx.opts); 366 | fuse_opt_free_args(&ctx.outargs); 367 | return res; 368 | } 369 | -------------------------------------------------------------------------------- /compat/fuse_opt.h: -------------------------------------------------------------------------------- 1 | /* 2 | FUSE: Filesystem in Userspace 3 | Copyright (C) 2001-2006 Miklos Szeredi 4 | 5 | This file is borrowed from sshfs-fuse. It provides an implementation 6 | of the FUSE command line parsing functions which only appear in 7 | later version of FUSE but are used in afuse. 8 | 9 | This file can be distributed under the terms of the GNU LGPL. 10 | See the file COPYING.LIB 11 | */ 12 | 13 | #ifndef _FUSE_OPT_H_ 14 | #define _FUSE_OPT_H_ 15 | 16 | /* This file defines the option parsing interface of FUSE */ 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /** 23 | * Option description 24 | * 25 | * This structure describes a single option, and and action associated 26 | * with it, in case it matches. 27 | * 28 | * More than one such match may occur, in which case the action for 29 | * each match is executed. 30 | * 31 | * There are three possible actions in case of a match: 32 | * 33 | * i) An integer (int or unsigned) variable determined by 'offset' is 34 | * set to 'value' 35 | * 36 | * ii) The processing function is called, with 'value' as the key 37 | * 38 | * iii) An integer (any) or string (char *) variable determined by 39 | * 'offset' is set to the value of an option parameter 40 | * 41 | * 'offset' should normally be either set to 42 | * 43 | * - 'offsetof(struct foo, member)' actions i) and iii) 44 | * 45 | * - -1 action ii) 46 | * 47 | * The 'offsetof()' macro is defined in the header. 48 | * 49 | * The template determines which options match, and also have an 50 | * effect on the action. Normally the action is either i) or ii), but 51 | * if a format is present in the template, then action iii) is 52 | * performed. 53 | * 54 | * The types of templates are: 55 | * 56 | * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only 57 | * themselves. Invalid values are "--" and anything beginning 58 | * with "-o" 59 | * 60 | * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or 61 | * the relevant option in a comma separated option list 62 | * 63 | * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) 64 | * which have a parameter 65 | * 66 | * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform 67 | * action iii). 68 | * 69 | * 5) "-x ", etc. Matches either "-xparam" or "-x param" as 70 | * two separate arguments 71 | * 72 | * 6) "-x %s", etc. Combination of 4) and 5) 73 | * 74 | * If the format is "%s", memory is allocated for the string unlike 75 | * with scanf(). 76 | */ 77 | struct fuse_opt { 78 | /** Matching template and optional parameter formatting */ 79 | const char *template; 80 | 81 | /** 82 | * Offset of variable within 'data' parameter of fuse_opt_parse() 83 | * or -1 84 | */ 85 | unsigned long offset; 86 | 87 | /** 88 | * Value to set the variable to, or to be passed as 'key' to the 89 | * processing function. Ignored if template a format 90 | */ 91 | int value; 92 | }; 93 | 94 | /** 95 | * Key option. In case of a match, the processing function will be 96 | * called with the specified key. 97 | */ 98 | #define FUSE_OPT_KEY(template, key) { template, -1U, key } 99 | 100 | /** 101 | * Last option. An array of 'struct fuse_opt' must end with a NULL 102 | * template value 103 | */ 104 | #define FUSE_OPT_END { .template = NULL } 105 | 106 | /** 107 | * Argument list 108 | */ 109 | struct fuse_args { 110 | /** Argument count */ 111 | int argc; 112 | 113 | /** Argument vector. NULL terminated */ 114 | char **argv; 115 | 116 | /** Is 'argv' allocated? */ 117 | int allocated; 118 | }; 119 | 120 | /** 121 | * Initializer for 'struct fuse_args' 122 | */ 123 | #define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } 124 | 125 | /** 126 | * Key value passed to the processing function if an option did not 127 | * match any templated 128 | */ 129 | #define FUSE_OPT_KEY_OPT -1 130 | 131 | /** 132 | * Key value passed to the processing function for all non-options 133 | * 134 | * Non-options are the arguments beginning with a charater other than 135 | * '-' or all arguments after the special '--' option 136 | */ 137 | #define FUSE_OPT_KEY_NONOPT -2 138 | 139 | /** 140 | * Processing function 141 | * 142 | * This function is called if 143 | * - option did not match any 'struct fuse_opt' 144 | * - argument is a non-option 145 | * - option did match and offset was set to -1 146 | * 147 | * The 'arg' parameter will always contain the whole argument or 148 | * option including the parameter if exists. A two-argument option 149 | * ("-x foo") is always converted to single arguemnt option of the 150 | * form "-xfoo" before this function is called. 151 | * 152 | * Options of the form '-ofoo' are passed to this function without the 153 | * '-o' prefix. 154 | * 155 | * The return value of this function determines whether this argument 156 | * is to be inserted into the output argument vector, or discarded. 157 | * 158 | * @param data is the user data passed to the fuse_opt_parse() function 159 | * @param arg is the whole argument or option 160 | * @param key determines why the processing function was called 161 | * @param outargs the current output argument list 162 | * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept 163 | */ 164 | typedef int (*fuse_opt_proc_t) (void *data, const char *arg, int key, 165 | struct fuse_args * outargs); 166 | 167 | /** 168 | * Option parsing function 169 | * 170 | * If 'args' was returned from a previous call to fuse_opt_parse() or 171 | * it was constructed from 172 | * 173 | * A NULL 'args' is equivalent to an empty argument vector 174 | * 175 | * A NULL 'opts' is equivalent to an 'opts' array containing a single 176 | * end marker 177 | * 178 | * A NULL 'proc' is equivalent to a processing function always 179 | * returning '1' 180 | * 181 | * @param args is the input and output argument list 182 | * @param data is the user data 183 | * @param opts is the option description array 184 | * @param proc is the processing function 185 | * @return -1 on error, 0 on success 186 | */ 187 | int fuse_opt_parse(struct fuse_args *args, void *data, 188 | const struct fuse_opt opts[], fuse_opt_proc_t proc); 189 | 190 | /** 191 | * Add an option to a comma separated option list 192 | * 193 | * @param opts is a pointer to an option list, may point to a NULL value 194 | * @param opt is the option to add 195 | * @return -1 on allocation error, 0 on success 196 | */ 197 | int fuse_opt_add_opt(char **opts, const char *opt); 198 | 199 | /** 200 | * Add an argument to a NULL terminated argument vector 201 | * 202 | * @param args is the structure containing the current argument list 203 | * @param arg is the new argument to add 204 | * @return -1 on allocation error, 0 on success 205 | */ 206 | int fuse_opt_add_arg(struct fuse_args *args, const char *arg); 207 | 208 | /** 209 | * Free the contents of argument list 210 | * 211 | * The structure itself is not freed 212 | * 213 | * @param args is the structure containing the argument list 214 | */ 215 | void fuse_opt_free_args(struct fuse_args *args); 216 | 217 | /** 218 | * Check if an option matches 219 | * 220 | * @param opts is the option description array 221 | * @param opt is the option to match 222 | * @return 1 if a match is found, 0 if not 223 | */ 224 | int fuse_opt_match(const struct fuse_opt opts[], const char *opt); 225 | 226 | #ifdef __cplusplus 227 | } 228 | #endif 229 | #endif /* _FUSE_OPT_H_ */ 230 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.59) 5 | AC_INIT([afuse], [0.5.0]) 6 | 7 | AM_INIT_AUTOMAKE 8 | AC_PROG_RANLIB 9 | 10 | AC_CONFIG_SRCDIR([src/afuse.c]) 11 | AC_CONFIG_HEADERS([config.h]) 12 | 13 | # Checks for programs. 14 | AC_PROG_CC 15 | 16 | # Checks for libraries. 17 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH 18 | PKG_CHECK_MODULES([FUSE], [fuse >= 2.3]) 19 | CFLAGS="$CFLAGS -Wall -Wextra $FUSE_CFLAGS -DFUSE_USE_VERSION=25" 20 | LIBS="$FUSE_LIBS" 21 | 22 | # Check if we need to enable compatibility code for old FUSE versions 23 | have_fuse_opt_parse=no 24 | AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes]) 25 | if test "$have_fuse_opt_parse" = no; then 26 | CFLAGS="$CFLAGS -I$PWD/compat" 27 | fi 28 | AM_CONDITIONAL(FUSE_OPT_COMPAT, test "$have_fuse_opt_parse" = no) 29 | 30 | 31 | AC_CHECK_FUNCS([setxattr fdatasync getline fgetln]) 32 | 33 | AC_CONFIG_FILES([Makefile 34 | src/Makefile 35 | compat/Makefile]) 36 | 37 | if test "x$ac_cv_func_getline" = "xno" -a "x$ac_cv_func_fgetln" = "xno"; then 38 | AC_MSG_ERROR([Neither getline nor fgetln are available, we can't compile]) 39 | fi 40 | 41 | AC_OUTPUT 42 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_bin_SCRIPTS=afuse-avahissh 2 | bin_PROGRAMS=afuse 3 | afuse_SOURCES=afuse.c afuse.h fd_list.c fd_list.h dir_list.c dir_list.h utils.c utils.h variable_pairing_heap.h string_sorted_list.c string_sorted_list.h 4 | 5 | if FUSE_OPT_COMPAT 6 | afuse_LDADD = ../compat/libcompat.a 7 | endif 8 | -------------------------------------------------------------------------------- /src/afuse-avahissh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cwd="`dirname "$0"`" 4 | 5 | mnt_templ="sshfs -o BatchMode=yes %r:/ %m" 6 | unmnt_templ="fusermount -u -z %m" 7 | pop_root_cmd="avahi-browse -trkp _sftp-ssh._tcp | grep '^=' | cut -d';' -f7" 8 | timeout="10s" 9 | 10 | "$cwd"/afuse -o mount_template="$mnt_templ" -o unmount_template="$unmnt_templ" -o populate_root_command="$pop_root_cmd" -o fsname=sshnet -o timeout="$timeout" "$@" 11 | -------------------------------------------------------------------------------- /src/afuse.c: -------------------------------------------------------------------------------- 1 | /* 2 | afuse - An automounter using FUSE 3 | Copyright (C) 2008-2013 Jacob Bower 4 | 5 | Portions of this program derive from examples provided with 6 | FUSE-2.5.2. 7 | 8 | This program can be distributed under the terms of the GNU GPL. 9 | See the file COPYING. 10 | */ 11 | 12 | #include 13 | 14 | #ifdef linux 15 | // For pread()/pwrite() 16 | #define _XOPEN_SOURCE 500 17 | // For getline() 18 | #define _GNU_SOURCE 19 | #endif 20 | 21 | #include 22 | #include 23 | #ifndef __USE_BSD 24 | // for mkdtemp 25 | #define __USE_BSD 26 | #endif 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #ifdef HAVE_SETXATTR 44 | #include 45 | 46 | #if !HAVE_GETLINE && !HAVE_FGETLN 47 | #error Need getline or fgetln 48 | #endif 49 | 50 | #ifdef XATTR_NOFOLLOW 51 | #define lgetxattr(p, n, v, s) \ 52 | getxattr(p, n, v, s, 0, XATTR_NOFOLLOW) 53 | #define lsetxattr(p, n, v, s, f) \ 54 | setxattr(p, n, v, s, 0, f | XATTR_NOFOLLOW) 55 | #define llistxattr(p, list, s) \ 56 | listxattr(p, list, s, XATTR_NOFOLLOW) 57 | #define lremovexattr(p, n) \ 58 | removexattr(p, n, XATTR_NOFOLLOW) 59 | #endif /* XATTR_NOFOLLOW */ 60 | #endif /* HAVE_SETXATTR */ 61 | 62 | #include "fd_list.h" 63 | #include "dir_list.h" 64 | #include "string_sorted_list.h" 65 | #include "utils.h" 66 | 67 | #include "variable_pairing_heap.h" 68 | 69 | #define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX" 70 | #define TMP_DIR_TEMPLATE2 "/afuse-XXXXXX" 71 | static char *mount_point_directory; 72 | static dev_t mount_point_dev; 73 | 74 | // Data structure filled in when parsing command line args 75 | struct user_options_t { 76 | char *mount_command_template; 77 | char *unmount_command_template; 78 | char *populate_root_command; 79 | char *filter_file; 80 | bool flush_writes; 81 | bool exact_getattr; 82 | uint64_t auto_unmount_delay; 83 | char *mount_dir; 84 | } user_options = { 85 | NULL, NULL, NULL, NULL, false, false, UINT64_MAX, NULL 86 | }; 87 | 88 | typedef struct _mount_list_t { 89 | struct _mount_list_t *next; 90 | struct _mount_list_t *prev; 91 | 92 | char *root_name; 93 | char *mount_point; 94 | fd_list_t *fd_list; 95 | dir_list_t *dir_list; 96 | 97 | PH_NEW_LINK(struct _mount_list_t) auto_unmount_ph_node; 98 | /* This is the sort key for the auto_unmount_ph heap. It will 99 | equal UINT64_MAX if this node is not in the heap. */ 100 | int64_t auto_unmount_time; 101 | } mount_list_t; 102 | 103 | typedef struct _mount_filter_list_t { 104 | struct _mount_filter_list_t *next; 105 | 106 | char *pattern; 107 | } mount_filter_list_t; 108 | 109 | PH_DECLARE_TYPE(auto_unmount_ph, mount_list_t) 110 | PH_DEFINE_TYPE(auto_unmount_ph, mount_list_t, auto_unmount_ph_node, 111 | auto_unmount_time) 112 | static mount_filter_list_t *mount_filter_list = NULL; 113 | 114 | #define BLOCK_SIGALRM \ 115 | sigset_t block_sigalrm_oldset, block_sigalrm_set; \ 116 | sigemptyset(&block_sigalrm_set); \ 117 | sigaddset(&block_sigalrm_set, SIGALRM); \ 118 | sigprocmask(SIG_BLOCK, &block_sigalrm_set, &block_sigalrm_oldset) 119 | 120 | #define UNBLOCK_SIGALRM \ 121 | sigprocmask(SIG_SETMASK, &block_sigalrm_oldset, NULL) 122 | 123 | #define DEFAULT_CASE_INVALID_ENUM \ 124 | fprintf(stderr, "Unexpected switch value in %s:%s:%d\n", \ 125 | __FILE__, __func__, __LINE__); \ 126 | exit(1); 127 | 128 | static auto_unmount_ph_t auto_unmount_ph; 129 | static int64_t auto_unmount_next_timeout = INT64_MAX; 130 | 131 | static void add_mount_filter(const char *glob) 132 | { 133 | mount_filter_list_t *new_entry; 134 | 135 | new_entry = my_malloc(sizeof(mount_filter_list_t)); 136 | new_entry->pattern = my_strdup(glob); 137 | new_entry->next = mount_filter_list; 138 | 139 | mount_filter_list = new_entry; 140 | } 141 | 142 | static int is_mount_filtered(const char *mount_point) 143 | { 144 | mount_filter_list_t *current_filter; 145 | 146 | current_filter = mount_filter_list; 147 | 148 | while (current_filter) { 149 | if (!fnmatch(current_filter->pattern, mount_point, 0)) 150 | return 1; 151 | 152 | current_filter = current_filter->next; 153 | } 154 | 155 | return 0; 156 | } 157 | 158 | static void load_mount_filter_file(const char *filename) 159 | { 160 | FILE *filter_file; 161 | if ((filter_file = fopen(filename, "r")) == NULL) { 162 | fprintf(stderr, "Failed to open filter file '%s'\n", filename); 163 | exit(1); 164 | } 165 | 166 | char *line = NULL; 167 | ssize_t llen; 168 | size_t lsize; 169 | #ifdef HAVE_GETLINE 170 | while ((llen = getline(&line, &lsize, filter_file)) != -1) 171 | #else // HAVE_FGETLN 172 | while (line = fgetln(filter_file, &llen)) 173 | #endif 174 | { 175 | if (llen >= 1) { 176 | if (line[0] == '#') 177 | continue; 178 | 179 | if (line[llen - 1] == '\n') { 180 | line[llen - 1] = '\0'; 181 | llen--; 182 | } 183 | } 184 | 185 | if (llen > 0) 186 | add_mount_filter(line); 187 | } 188 | 189 | free(line); 190 | 191 | fclose(filter_file); 192 | } 193 | 194 | static int64_t from_timeval(const struct timeval *tv) 195 | { 196 | return (int64_t) tv->tv_sec * 1000000 + ((int64_t) tv->tv_usec); 197 | } 198 | 199 | static void to_timeval(struct timeval *tv, int64_t usec) 200 | { 201 | tv->tv_sec = usec / 1000000; 202 | tv->tv_usec = usec % 1000000; 203 | } 204 | 205 | static int get_retval(int res) 206 | { 207 | if (res == -1) 208 | return -errno; 209 | return 0; 210 | } 211 | 212 | static int check_mount(mount_list_t * mount) 213 | { 214 | struct stat buf; 215 | 216 | if (lstat(mount->mount_point, &buf) == -1) 217 | return 0; 218 | if (buf.st_dev == mount_point_dev) 219 | return 0; 220 | return 1; 221 | } 222 | 223 | static void update_auto_unmount(mount_list_t * mount) 224 | { 225 | if (user_options.auto_unmount_delay == UINT64_MAX) 226 | return; 227 | 228 | /* Get the current time */ 229 | struct timeval tv; 230 | mount_list_t *min_mount; 231 | int64_t cur_time, next_time; 232 | gettimeofday(&tv, NULL); 233 | cur_time = from_timeval(&tv); 234 | 235 | if (mount) { 236 | /* Always remove first */ 237 | if (mount->auto_unmount_time != INT64_MAX) 238 | auto_unmount_ph_remove(&auto_unmount_ph, mount); 239 | 240 | if (!mount->fd_list && !mount->dir_list) { 241 | mount->auto_unmount_time = 242 | cur_time + user_options.auto_unmount_delay; 243 | auto_unmount_ph_insert(&auto_unmount_ph, mount); 244 | } else { 245 | mount->auto_unmount_time = INT64_MAX; 246 | } 247 | } 248 | min_mount = auto_unmount_ph_min(&auto_unmount_ph); 249 | next_time = min_mount ? min_mount->auto_unmount_time : INT64_MAX; 250 | 251 | if (next_time != auto_unmount_next_timeout) { 252 | struct itimerval itv; 253 | auto_unmount_next_timeout = next_time; 254 | 255 | if (next_time != INT64_MAX) { 256 | if (next_time > cur_time) 257 | to_timeval(&itv.it_value, next_time - cur_time); 258 | else /* Timer is set to expire immediately --- set it to 1 instead */ 259 | to_timeval(&itv.it_value, 1); 260 | } else { 261 | /* Disable the timer */ 262 | to_timeval(&itv.it_value, 0); 263 | } 264 | to_timeval(&itv.it_interval, 0); 265 | if (setitimer(ITIMER_REAL, &itv, NULL) != 0) { 266 | perror("Error setting timer"); 267 | } 268 | } 269 | } 270 | 271 | int do_umount(mount_list_t * mount); 272 | 273 | static void handle_auto_unmount_timer(int x) 274 | { 275 | (void)x; /* Ignored */ 276 | /* Get the current time */ 277 | struct timeval tv; 278 | int64_t cur_time; 279 | mount_list_t *mount; 280 | gettimeofday(&tv, NULL); 281 | cur_time = from_timeval(&tv); 282 | 283 | while ((mount = auto_unmount_ph_min(&auto_unmount_ph)) != NULL && 284 | mount->auto_unmount_time <= cur_time) { 285 | do_umount(mount); 286 | } 287 | 288 | update_auto_unmount(NULL); 289 | } 290 | 291 | mount_list_t *mount_list = NULL; 292 | 293 | mount_list_t *find_mount(const char *root_name) 294 | { 295 | mount_list_t *current_mount = mount_list; 296 | 297 | while (current_mount) { 298 | if (strcmp(root_name, current_mount->root_name) == 0) 299 | return current_mount; 300 | 301 | current_mount = current_mount->next; 302 | } 303 | 304 | return NULL; 305 | } 306 | 307 | int is_mount(const char *root_name) 308 | { 309 | return find_mount(root_name) ? 1 : 0; 310 | } 311 | 312 | mount_list_t *add_mount(const char *root_name, char *mount_point) 313 | { 314 | mount_list_t *new_mount; 315 | 316 | new_mount = (mount_list_t *) my_malloc(sizeof(mount_list_t)); 317 | new_mount->root_name = my_strdup(root_name); 318 | new_mount->mount_point = mount_point; 319 | 320 | new_mount->next = mount_list; 321 | new_mount->prev = NULL; 322 | new_mount->fd_list = NULL; 323 | new_mount->dir_list = NULL; 324 | new_mount->auto_unmount_time = INT64_MAX; 325 | if (mount_list) 326 | mount_list->prev = new_mount; 327 | 328 | mount_list = new_mount; 329 | 330 | update_auto_unmount(new_mount); 331 | 332 | return new_mount; 333 | } 334 | 335 | void remove_mount(mount_list_t * current_mount) 336 | { 337 | if (current_mount->auto_unmount_time != INT64_MAX) 338 | auto_unmount_ph_remove(&auto_unmount_ph, current_mount); 339 | 340 | free(current_mount->root_name); 341 | free(current_mount->mount_point); 342 | if (current_mount->prev) 343 | current_mount->prev->next = current_mount->next; 344 | else 345 | mount_list = current_mount->next; 346 | if (current_mount->next) 347 | current_mount->next->prev = current_mount->prev; 348 | free(current_mount); 349 | update_auto_unmount(NULL); 350 | } 351 | 352 | char *make_mount_point(const char *root_name) 353 | { 354 | char *dir_tmp; 355 | 356 | // Create the mount point 357 | dir_tmp = 358 | my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name)); 359 | strcpy(dir_tmp, mount_point_directory); 360 | strcat(dir_tmp, "/"); 361 | strcat(dir_tmp, root_name); 362 | 363 | if (mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) { 364 | fprintf(stderr, "Cannot create directory: %s (%s)\n", 365 | dir_tmp, strerror(errno)); 366 | free(dir_tmp); 367 | return NULL; 368 | } 369 | return dir_tmp; 370 | } 371 | 372 | // Note: this method strips out quotes and applies them itself as should be appropriate 373 | bool run_template(const char *template, const char *mount_point, 374 | const char *root_name) 375 | { 376 | int len = 0; 377 | int nargs = 1; 378 | int i; 379 | char *buf; 380 | char *p; 381 | char **args; 382 | char **arg; 383 | bool quote = false; 384 | pid_t pid; 385 | int status; 386 | 387 | // calculate length 388 | for (i = 0; template[i]; i++) 389 | if (template[i] == '%') { 390 | switch (template[i + 1]) { 391 | case 'm': 392 | len += strlen(mount_point); 393 | i++; 394 | break; 395 | case 'r': 396 | len += strlen(root_name); 397 | i++; 398 | break; 399 | case '%': 400 | len++; 401 | i++; 402 | break; 403 | } 404 | } else if (template[i] == ' ' && !quote) { 405 | len++; 406 | nargs++; 407 | } else if (template[i] == '"') 408 | quote = !quote; 409 | else if (template[i] == '\\' && template[i + 1]) 410 | len++, i++; 411 | else 412 | len++; 413 | 414 | buf = my_malloc(len + 1); 415 | args = my_malloc((nargs + 1) * sizeof(*args)); 416 | 417 | p = buf; 418 | arg = args; 419 | *arg++ = p; 420 | 421 | for (i = 0; template[i]; i++) 422 | if (template[i] == '%') { 423 | switch (template[i + 1]) { 424 | case 'm': 425 | strcpy(p, mount_point); 426 | p += strlen(mount_point); 427 | i++; 428 | break; 429 | case 'r': 430 | strcpy(p, root_name); 431 | p += strlen(root_name); 432 | i++; 433 | break; 434 | case '%': 435 | *p++ = '%'; 436 | i++; 437 | break; 438 | } 439 | } else if (template[i] == ' ' && !quote) { 440 | *p++ = '\0'; 441 | *arg++ = p; 442 | } else if (template[i] == '"') 443 | quote = !quote; 444 | else if (template[i] == '\\' && template[i + 1]) 445 | *p++ = template[++i]; 446 | else 447 | *p++ = template[i]; 448 | 449 | *p = '\0'; 450 | *arg = NULL; 451 | 452 | pid = fork(); 453 | if (pid == -1) { 454 | fprintf(stderr, "Failed to fork (%s)\n", strerror(errno)); 455 | free(args); 456 | free(buf); 457 | return false; 458 | } 459 | if (pid == 0) { 460 | execvp(args[0], args); 461 | abort(); 462 | } 463 | pid = waitpid(pid, &status, 0); 464 | if (pid == -1) { 465 | fprintf(stderr, "Failed to waitpid (%s)\n", strerror(errno)); 466 | free(args); 467 | free(buf); 468 | return false; 469 | } 470 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 471 | fprintf(stderr, "Failed to invoke command: %s\n", args[0]); 472 | free(args); 473 | free(buf); 474 | return false; 475 | } 476 | free(args); 477 | free(buf); 478 | return true; 479 | } 480 | 481 | mount_list_t *do_mount(const char *root_name) 482 | { 483 | char *mount_point; 484 | mount_list_t *mount; 485 | 486 | fprintf(stderr, "Mounting: %s\n", root_name); 487 | 488 | if (!(mount_point = make_mount_point(root_name))) { 489 | fprintf(stderr, 490 | "Failed to create mount point directory: %s/%s\n", 491 | mount_point_directory, root_name); 492 | return NULL; 493 | } 494 | 495 | if (!run_template(user_options.mount_command_template, 496 | mount_point, root_name)) { 497 | // remove the now unused directory 498 | if (rmdir(mount_point) == -1) 499 | fprintf(stderr, 500 | "Failed to remove mount point dir: %s (%s)", 501 | mount_point, strerror(errno)); 502 | 503 | free(mount_point); 504 | return NULL; 505 | } 506 | 507 | mount = add_mount(root_name, mount_point); 508 | return mount; 509 | } 510 | 511 | int do_umount(mount_list_t * mount) 512 | { 513 | fprintf(stderr, "Unmounting: %s\n", mount->root_name); 514 | 515 | run_template(user_options.unmount_command_template, 516 | mount->mount_point, mount->root_name); 517 | /* Still unmount anyway */ 518 | 519 | if (rmdir(mount->mount_point) == -1) 520 | fprintf(stderr, "Failed to remove mount point dir: %s (%s)", 521 | mount->mount_point, strerror(errno)); 522 | remove_mount(mount); 523 | return 1; 524 | } 525 | 526 | void unmount_all(void) 527 | { 528 | fprintf(stderr, "Attempting to unmount all filesystems:\n"); 529 | 530 | while (mount_list) { 531 | fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name); 532 | 533 | do_umount(mount_list); 534 | } 535 | 536 | fprintf(stderr, "done.\n"); 537 | } 538 | 539 | void shutdown(void) 540 | { 541 | BLOCK_SIGALRM; 542 | 543 | unmount_all(); 544 | 545 | UNBLOCK_SIGALRM; 546 | 547 | if (rmdir(mount_point_directory) == -1) 548 | fprintf(stderr, 549 | "Failed to remove temporary mount point directory: %s (%s)\n", 550 | mount_point_directory, strerror(errno)); 551 | } 552 | 553 | int max_path_out_len(const char *path_in) 554 | { 555 | return strlen(mount_point_directory) + strlen(path_in) + 2; 556 | } 557 | 558 | // returns true if path is a child directory of a root node 559 | // e.g. /a/b is a child, /a is not. 560 | int extract_root_name(const char *path, char *root_name) 561 | { 562 | int i; 563 | 564 | for (i = 1; path[i] && path[i] != '/'; i++) 565 | root_name[i - 1] = path[i]; 566 | root_name[i - 1] = '\0'; 567 | 568 | return strlen(&path[i]); 569 | } 570 | 571 | typedef enum { 572 | PROC_PATH_FAILED, 573 | PROC_PATH_ROOT_DIR, 574 | PROC_PATH_ROOT_SUBDIR, 575 | PROC_PATH_PROXY_DIR 576 | } proc_result_t; 577 | 578 | proc_result_t process_path(const char *path_in, char *path_out, char *root_name, 579 | int attempt_mount, mount_list_t ** out_mount) 580 | { 581 | char *path_out_base; 582 | int is_child; 583 | int len; 584 | mount_list_t *mount = NULL; 585 | 586 | *out_mount = NULL; 587 | 588 | fprintf(stderr, "Path in: %s\n", path_in); 589 | is_child = extract_root_name(path_in, root_name); 590 | fprintf(stderr, "root_name is: %s\n", root_name); 591 | 592 | if (is_mount_filtered(root_name)) 593 | return PROC_PATH_FAILED; 594 | 595 | // Mount filesystem if necessary 596 | // the combination of is_child and attempt_mount prevent inappropriate 597 | // mounting of a filesystem for example if the user tries to mknod 598 | // in the afuse root this should cause an error not a mount. 599 | // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr 600 | // on the root node seems to occur with every single access. 601 | if ((is_child || attempt_mount) && 602 | strlen(root_name) > 0 && 603 | !(mount = find_mount(root_name)) && !(mount = do_mount(root_name))) 604 | return PROC_PATH_FAILED; 605 | 606 | if (mount && !check_mount(mount)) { 607 | do_umount(mount); 608 | mount = do_mount(root_name); 609 | if (!mount) 610 | return PROC_PATH_FAILED; 611 | } 612 | // construct path_out (mount_point_directory + '/' + path_in + '\0') 613 | path_out_base = path_out; 614 | len = strlen(mount_point_directory); 615 | memcpy(path_out, mount_point_directory, len); 616 | path_out += len; 617 | *path_out++ = '/'; 618 | len = strlen(path_in) - 1; 619 | memcpy(path_out, path_in + 1, len); 620 | path_out += len; 621 | *path_out = '\0'; 622 | fprintf(stderr, "Path out: %s\n", path_out_base); 623 | 624 | *out_mount = mount; 625 | 626 | if (is_child) 627 | return PROC_PATH_PROXY_DIR; 628 | else if (strlen(root_name)) 629 | return PROC_PATH_ROOT_SUBDIR; 630 | else 631 | return PROC_PATH_ROOT_DIR; 632 | } 633 | 634 | static int afuse_getattr(const char *path, struct stat *stbuf) 635 | { 636 | char *root_name = alloca(strlen(path)); 637 | char *real_path = alloca(max_path_out_len(path)); 638 | int retval; 639 | mount_list_t *mount; 640 | BLOCK_SIGALRM; 641 | 642 | fprintf(stderr, "> GetAttr\n"); 643 | 644 | switch (process_path(path, real_path, root_name, 0, &mount)) { 645 | case PROC_PATH_FAILED: 646 | retval = -ENXIO; 647 | break; 648 | 649 | case PROC_PATH_ROOT_DIR: 650 | fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name); 651 | stbuf->st_mode = S_IFDIR | 0700; 652 | stbuf->st_nlink = 1; 653 | stbuf->st_uid = getuid(); 654 | stbuf->st_gid = getgid(); 655 | stbuf->st_size = 0; 656 | stbuf->st_blksize = 0; 657 | stbuf->st_blocks = 0; 658 | stbuf->st_atime = 0; 659 | stbuf->st_mtime = 0; 660 | stbuf->st_ctime = 0; 661 | retval = 0; 662 | break; 663 | case PROC_PATH_ROOT_SUBDIR: 664 | if (user_options.exact_getattr) 665 | /* try to mount it */ 666 | process_path(path, real_path, root_name, 1, &mount); 667 | if (!mount) { 668 | stbuf->st_mode = S_IFDIR | 0000; 669 | if (!user_options.exact_getattr) 670 | stbuf->st_mode = S_IFDIR | 0750; 671 | stbuf->st_nlink = 1; 672 | stbuf->st_uid = getuid(); 673 | stbuf->st_gid = getgid(); 674 | stbuf->st_size = 0; 675 | stbuf->st_blksize = 0; 676 | stbuf->st_blocks = 0; 677 | stbuf->st_atime = 0; 678 | stbuf->st_mtime = 0; 679 | stbuf->st_ctime = 0; 680 | retval = 0; 681 | break; 682 | } 683 | 684 | case PROC_PATH_PROXY_DIR: 685 | retval = get_retval(lstat(real_path, stbuf)); 686 | break; 687 | 688 | default: 689 | DEFAULT_CASE_INVALID_ENUM; 690 | } 691 | if (mount) 692 | update_auto_unmount(mount); 693 | UNBLOCK_SIGALRM; 694 | return retval; 695 | } 696 | 697 | static int afuse_readlink(const char *path, char *buf, size_t size) 698 | { 699 | int res; 700 | char *root_name = alloca(strlen(path)); 701 | char *real_path = alloca(max_path_out_len(path)); 702 | int retval; 703 | mount_list_t *mount; 704 | BLOCK_SIGALRM; 705 | 706 | switch (process_path(path, real_path, root_name, 1, &mount)) { 707 | case PROC_PATH_FAILED: 708 | retval = -ENXIO; 709 | break; 710 | case PROC_PATH_ROOT_DIR: 711 | retval = -ENOENT; 712 | break; 713 | case PROC_PATH_ROOT_SUBDIR: 714 | if (!mount) { 715 | retval = -ENOENT; 716 | break; 717 | } 718 | case PROC_PATH_PROXY_DIR: 719 | res = readlink(real_path, buf, size - 1); 720 | if (res == -1) { 721 | retval = -errno; 722 | break; 723 | } 724 | buf[res] = '\0'; 725 | retval = 0; 726 | break; 727 | 728 | default: 729 | DEFAULT_CASE_INVALID_ENUM; 730 | } 731 | if (mount) 732 | update_auto_unmount(mount); 733 | UNBLOCK_SIGALRM; 734 | return retval; 735 | } 736 | 737 | static int afuse_opendir(const char *path, struct fuse_file_info *fi) 738 | { 739 | DIR *dp; 740 | char *root_name = alloca(strlen(path)); 741 | mount_list_t *mount; 742 | char *real_path = alloca(max_path_out_len(path)); 743 | int retval; 744 | BLOCK_SIGALRM; 745 | 746 | switch (process_path(path, real_path, root_name, 1, &mount)) { 747 | case PROC_PATH_FAILED: 748 | retval = -ENXIO; 749 | break; 750 | case PROC_PATH_ROOT_DIR: 751 | retval = 0; 752 | break; 753 | case PROC_PATH_ROOT_SUBDIR: 754 | if (!mount) { 755 | retval = -EACCES; 756 | fi->fh = 0lu; 757 | break; 758 | } 759 | case PROC_PATH_PROXY_DIR: 760 | dp = opendir(real_path); 761 | if (dp == NULL) { 762 | retval = -errno; 763 | break; 764 | } 765 | fi->fh = (unsigned long)dp; 766 | if (mount) 767 | dir_list_add(&mount->dir_list, dp); 768 | retval = 0; 769 | break; 770 | 771 | default: 772 | DEFAULT_CASE_INVALID_ENUM; 773 | } 774 | if (mount) 775 | update_auto_unmount(mount); 776 | UNBLOCK_SIGALRM; 777 | return retval; 778 | } 779 | 780 | static inline DIR *get_dirp(struct fuse_file_info *fi) 781 | { 782 | return (DIR *) (uintptr_t) fi->fh; 783 | } 784 | 785 | int populate_root_dir(char *pop_cmd, struct list_t **dir_entry_listptr, 786 | fuse_fill_dir_t filler, void *buf) 787 | { 788 | FILE *browser; 789 | size_t hsize = 0; 790 | ssize_t hlen; 791 | char *dir_entry = NULL; 792 | 793 | if (!pop_cmd) 794 | return -1; 795 | 796 | if ((browser = popen(pop_cmd, "r")) == NULL) { 797 | fprintf(stderr, "Failed to execute populate_root_command=%s\n", 798 | pop_cmd); 799 | return -errno; 800 | } 801 | 802 | int loop_error = 0; 803 | #ifdef HAVE_GETLINE 804 | while ((hlen = getline(&dir_entry, &hsize, browser)) != -1) 805 | #else // HAVE_FGETLN 806 | while (dir_entry = fgetln(browser, &hsize)) 807 | #endif 808 | { 809 | if (hlen >= 1 && dir_entry[hlen - 1] == '\n') 810 | dir_entry[hlen - 1] = '\0'; 811 | 812 | fprintf(stderr, "Got entry \"%s\"\n", dir_entry); 813 | 814 | int insert_err = 815 | insert_sorted_if_unique(dir_entry_listptr, dir_entry); 816 | if (insert_err == 1) // already in list 817 | continue; 818 | else if (insert_err) { 819 | fprintf(stderr, 820 | "populate_root_command: failed on inserting new entry into sorted list.\n"); 821 | loop_error = 1; 822 | } 823 | 824 | if (strlen(dir_entry) != 0) 825 | filler(buf, dir_entry, NULL, 0); 826 | } 827 | 828 | free(dir_entry); 829 | 830 | int pclose_err = pclose(browser); 831 | if (pclose_err) { 832 | int pclose_errno = errno; 833 | fprintf(stderr, 834 | "populate_root_command: pclose failed, ret %d, status %d, errno %d (%s)\n", 835 | pclose_errno, WEXITSTATUS(pclose_errno), pclose_errno, 836 | strerror(pclose_errno)); 837 | } 838 | 839 | return loop_error || pclose_err; 840 | } 841 | 842 | static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, 843 | off_t offset, struct fuse_file_info *fi) 844 | { 845 | DIR *dp = get_dirp(fi); 846 | struct dirent *de; 847 | char *root_name = alloca(strlen(path)); 848 | char *real_path = alloca(max_path_out_len(path)); 849 | struct list_t *dir_entry_list = NULL; 850 | mount_list_t *mount, *next; 851 | int retval; 852 | BLOCK_SIGALRM; 853 | 854 | switch (process_path(path, real_path, root_name, 1, &mount)) { 855 | case PROC_PATH_FAILED: 856 | retval = -ENXIO; 857 | break; 858 | 859 | case PROC_PATH_ROOT_DIR: 860 | filler(buf, ".", NULL, 0); 861 | filler(buf, "..", NULL, 0); 862 | insert_sorted_if_unique(&dir_entry_list, "."); 863 | insert_sorted_if_unique(&dir_entry_list, ".."); 864 | for (mount = mount_list; mount; mount = next) { 865 | next = mount->next; 866 | /* Check for dead mounts. */ 867 | if (!check_mount(mount)) { 868 | do_umount(mount); 869 | } else { 870 | if (insert_sorted_if_unique 871 | (&dir_entry_list, mount->root_name)) 872 | retval = -1; 873 | filler(buf, mount->root_name, NULL, 0); 874 | } 875 | } 876 | populate_root_dir(user_options.populate_root_command, 877 | &dir_entry_list, filler, buf); 878 | destroy_list(&dir_entry_list); 879 | mount = NULL; 880 | retval = 0; 881 | break; 882 | 883 | case PROC_PATH_ROOT_SUBDIR: 884 | if (!mount) { 885 | retval = (!dp) ? -EBADF : -EACCES; 886 | break; 887 | } 888 | case PROC_PATH_PROXY_DIR: 889 | seekdir(dp, offset); 890 | while ((de = readdir(dp)) != NULL) { 891 | struct stat st; 892 | memset(&st, 0, sizeof(st)); 893 | st.st_ino = de->d_ino; 894 | st.st_mode = de->d_type << 12; 895 | if (filler(buf, de->d_name, &st, telldir(dp))) 896 | break; 897 | } 898 | retval = 0; 899 | break; 900 | 901 | default: 902 | DEFAULT_CASE_INVALID_ENUM; 903 | } 904 | if (mount) 905 | update_auto_unmount(mount); 906 | UNBLOCK_SIGALRM; 907 | return retval; 908 | } 909 | 910 | static int afuse_releasedir(const char *path, struct fuse_file_info *fi) 911 | { 912 | DIR *dp = get_dirp(fi); 913 | mount_list_t *mount; 914 | char *root_name = alloca(strlen(path)); 915 | char *real_path = alloca(max_path_out_len(path)); 916 | int retval; 917 | 918 | BLOCK_SIGALRM; 919 | 920 | switch (process_path(path, real_path, root_name, 1, &mount)) { 921 | case PROC_PATH_FAILED: 922 | retval = -ENXIO; 923 | break; 924 | 925 | case PROC_PATH_ROOT_DIR: 926 | retval = 0; 927 | break; 928 | 929 | case PROC_PATH_ROOT_SUBDIR: 930 | case PROC_PATH_PROXY_DIR: 931 | if (mount) 932 | dir_list_remove(&mount->dir_list, dp); 933 | if (dp) 934 | closedir(dp); 935 | retval = 0; 936 | break; 937 | 938 | default: 939 | DEFAULT_CASE_INVALID_ENUM; 940 | } 941 | if (mount) 942 | update_auto_unmount(mount); 943 | UNBLOCK_SIGALRM; 944 | return retval; 945 | } 946 | 947 | static int afuse_mknod(const char *path, mode_t mode, dev_t rdev) 948 | { 949 | char *root_name = alloca(strlen(path)); 950 | char *real_path = alloca(max_path_out_len(path)); 951 | mount_list_t *mount; 952 | int retval; 953 | BLOCK_SIGALRM; 954 | fprintf(stderr, "> Mknod\n"); 955 | 956 | switch (process_path(path, real_path, root_name, 0, &mount)) { 957 | case PROC_PATH_FAILED: 958 | retval = -ENXIO; 959 | break; 960 | 961 | case PROC_PATH_ROOT_DIR: 962 | case PROC_PATH_ROOT_SUBDIR: 963 | retval = -ENOTSUP; 964 | break; 965 | 966 | case PROC_PATH_PROXY_DIR: 967 | if (S_ISFIFO(mode)) 968 | retval = get_retval(mkfifo(real_path, mode)); 969 | else 970 | retval = get_retval(mknod(real_path, mode, rdev)); 971 | break; 972 | 973 | default: 974 | DEFAULT_CASE_INVALID_ENUM; 975 | } 976 | if (mount) 977 | update_auto_unmount(mount); 978 | UNBLOCK_SIGALRM; 979 | return retval; 980 | } 981 | 982 | static int afuse_mkdir(const char *path, mode_t mode) 983 | { 984 | char *root_name = alloca(strlen(path)); 985 | char *real_path = alloca(max_path_out_len(path)); 986 | int retval; 987 | mount_list_t *mount; 988 | BLOCK_SIGALRM; 989 | 990 | switch (process_path(path, real_path, root_name, 0, &mount)) { 991 | case PROC_PATH_FAILED: 992 | retval = -ENXIO; 993 | break; 994 | case PROC_PATH_ROOT_DIR: 995 | case PROC_PATH_ROOT_SUBDIR: 996 | retval = -ENOTSUP; 997 | break; 998 | case PROC_PATH_PROXY_DIR: 999 | retval = get_retval(mkdir(real_path, mode)); 1000 | break; 1001 | 1002 | default: 1003 | DEFAULT_CASE_INVALID_ENUM; 1004 | } 1005 | if (mount) 1006 | update_auto_unmount(mount); 1007 | UNBLOCK_SIGALRM; 1008 | return retval; 1009 | } 1010 | 1011 | static int afuse_unlink(const char *path) 1012 | { 1013 | char *root_name = alloca(strlen(path)); 1014 | char *real_path = alloca(max_path_out_len(path)); 1015 | mount_list_t *mount; 1016 | int retval; 1017 | BLOCK_SIGALRM; 1018 | 1019 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1020 | case PROC_PATH_FAILED: 1021 | retval = -ENXIO; 1022 | break; 1023 | case PROC_PATH_ROOT_DIR: 1024 | case PROC_PATH_ROOT_SUBDIR: 1025 | retval = -ENOTSUP; 1026 | break; 1027 | case PROC_PATH_PROXY_DIR: 1028 | retval = get_retval(unlink(real_path)); 1029 | break; 1030 | 1031 | default: 1032 | DEFAULT_CASE_INVALID_ENUM; 1033 | } 1034 | if (mount) 1035 | update_auto_unmount(mount); 1036 | UNBLOCK_SIGALRM; 1037 | return retval; 1038 | } 1039 | 1040 | static int afuse_rmdir(const char *path) 1041 | { 1042 | char *root_name = alloca(strlen(path)); 1043 | char *real_path = alloca(max_path_out_len(path)); 1044 | mount_list_t *mount; 1045 | int retval; 1046 | BLOCK_SIGALRM; 1047 | 1048 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1049 | case PROC_PATH_FAILED: 1050 | retval = -ENXIO; 1051 | break; 1052 | case PROC_PATH_ROOT_DIR: 1053 | retval = -ENOTSUP; 1054 | break; 1055 | case PROC_PATH_ROOT_SUBDIR: 1056 | if (mount) { 1057 | /* Unmount */ 1058 | if (mount->dir_list || mount->fd_list) 1059 | retval = -EBUSY; 1060 | else { 1061 | do_umount(mount); 1062 | mount = NULL; 1063 | retval = 0; 1064 | } 1065 | } else 1066 | retval = -ENOTSUP; 1067 | break; 1068 | case PROC_PATH_PROXY_DIR: 1069 | retval = get_retval(rmdir(real_path)); 1070 | break; 1071 | 1072 | default: 1073 | DEFAULT_CASE_INVALID_ENUM; 1074 | } 1075 | if (mount) 1076 | update_auto_unmount(mount); 1077 | UNBLOCK_SIGALRM; 1078 | return retval; 1079 | } 1080 | 1081 | static int afuse_symlink(const char *from, const char *to) 1082 | { 1083 | char *root_name_to = alloca(strlen(to)); 1084 | char *real_to_path = alloca(max_path_out_len(to)); 1085 | mount_list_t *mount; 1086 | int retval; 1087 | BLOCK_SIGALRM; 1088 | 1089 | switch (process_path(to, real_to_path, root_name_to, 0, &mount)) { 1090 | case PROC_PATH_FAILED: 1091 | retval = -ENXIO; 1092 | break; 1093 | case PROC_PATH_ROOT_DIR: 1094 | case PROC_PATH_ROOT_SUBDIR: 1095 | retval = -ENOTSUP; 1096 | break; 1097 | case PROC_PATH_PROXY_DIR: 1098 | retval = get_retval(symlink(from, real_to_path)); 1099 | break; 1100 | 1101 | default: 1102 | DEFAULT_CASE_INVALID_ENUM; 1103 | } 1104 | if (mount) 1105 | update_auto_unmount(mount); 1106 | UNBLOCK_SIGALRM; 1107 | return retval; 1108 | } 1109 | 1110 | static int afuse_rename(const char *from, const char *to) 1111 | { 1112 | char *root_name_from = alloca(strlen(from)); 1113 | char *root_name_to = alloca(strlen(to)); 1114 | char *real_from_path = alloca(max_path_out_len(from)); 1115 | char *real_to_path = alloca(max_path_out_len(to)); 1116 | mount_list_t *mount_from, *mount_to = NULL; 1117 | int retval; 1118 | BLOCK_SIGALRM; 1119 | 1120 | switch (process_path 1121 | (from, real_from_path, root_name_from, 0, &mount_from)) { 1122 | 1123 | case PROC_PATH_FAILED: 1124 | retval = -ENXIO; 1125 | break; 1126 | 1127 | case PROC_PATH_ROOT_DIR: 1128 | case PROC_PATH_ROOT_SUBDIR: 1129 | retval = -ENOTSUP; 1130 | break; 1131 | 1132 | case PROC_PATH_PROXY_DIR: 1133 | switch (process_path 1134 | (to, real_to_path, root_name_to, 0, &mount_to)) { 1135 | 1136 | case PROC_PATH_FAILED: 1137 | retval = -ENXIO; 1138 | break; 1139 | 1140 | case PROC_PATH_ROOT_DIR: 1141 | case PROC_PATH_ROOT_SUBDIR: 1142 | retval = -ENOTSUP; 1143 | break; 1144 | 1145 | case PROC_PATH_PROXY_DIR: 1146 | retval = 1147 | get_retval(rename(real_from_path, real_to_path)); 1148 | break; 1149 | 1150 | default: 1151 | DEFAULT_CASE_INVALID_ENUM; 1152 | } 1153 | break; 1154 | 1155 | default: 1156 | DEFAULT_CASE_INVALID_ENUM; 1157 | } 1158 | if (mount_to) 1159 | update_auto_unmount(mount_to); 1160 | if (mount_from && mount_from != mount_to) 1161 | update_auto_unmount(mount_from); 1162 | UNBLOCK_SIGALRM; 1163 | return retval; 1164 | } 1165 | 1166 | static int afuse_link(const char *from, const char *to) 1167 | { 1168 | char *root_name_from = alloca(strlen(from)); 1169 | char *root_name_to = alloca(strlen(to)); 1170 | char *real_from_path = alloca(max_path_out_len(from)); 1171 | char *real_to_path = alloca(max_path_out_len(to)); 1172 | mount_list_t *mount_to = NULL, *mount_from; 1173 | int retval; 1174 | BLOCK_SIGALRM; 1175 | 1176 | switch (process_path 1177 | (from, real_from_path, root_name_from, 0, &mount_from)) { 1178 | 1179 | case PROC_PATH_FAILED: 1180 | retval = -ENXIO; 1181 | break; 1182 | case PROC_PATH_ROOT_DIR: 1183 | case PROC_PATH_ROOT_SUBDIR: 1184 | retval = -ENOTSUP; 1185 | break; 1186 | case PROC_PATH_PROXY_DIR: 1187 | switch (process_path 1188 | (to, real_to_path, root_name_to, 0, &mount_to)) { 1189 | 1190 | case PROC_PATH_FAILED: 1191 | retval = -ENXIO; 1192 | break; 1193 | case PROC_PATH_ROOT_DIR: 1194 | case PROC_PATH_ROOT_SUBDIR: 1195 | retval = -ENOTSUP; 1196 | break; 1197 | case PROC_PATH_PROXY_DIR: 1198 | retval = get_retval(link(real_from_path, real_to_path)); 1199 | break; 1200 | 1201 | default: 1202 | DEFAULT_CASE_INVALID_ENUM; 1203 | } 1204 | break; 1205 | 1206 | default: 1207 | DEFAULT_CASE_INVALID_ENUM; 1208 | } 1209 | if (mount_to) 1210 | update_auto_unmount(mount_to); 1211 | if (mount_from && mount_from != mount_to) 1212 | update_auto_unmount(mount_from); 1213 | UNBLOCK_SIGALRM; 1214 | return retval; 1215 | } 1216 | 1217 | static int afuse_chmod(const char *path, mode_t mode) 1218 | { 1219 | char *root_name = alloca(strlen(path)); 1220 | char *real_path = alloca(max_path_out_len(path)); 1221 | mount_list_t *mount; 1222 | int retval; 1223 | BLOCK_SIGALRM; 1224 | 1225 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1226 | case PROC_PATH_FAILED: 1227 | retval = -ENXIO; 1228 | break; 1229 | case PROC_PATH_ROOT_DIR: 1230 | case PROC_PATH_ROOT_SUBDIR: 1231 | retval = -ENOTSUP; 1232 | break; 1233 | case PROC_PATH_PROXY_DIR: 1234 | retval = get_retval(chmod(real_path, mode)); 1235 | break; 1236 | 1237 | default: 1238 | DEFAULT_CASE_INVALID_ENUM; 1239 | } 1240 | if (mount) 1241 | update_auto_unmount(mount); 1242 | UNBLOCK_SIGALRM; 1243 | return retval; 1244 | } 1245 | 1246 | static int afuse_chown(const char *path, uid_t uid, gid_t gid) 1247 | { 1248 | char *root_name = alloca(strlen(path)); 1249 | char *real_path = alloca(max_path_out_len(path)); 1250 | mount_list_t *mount; 1251 | int retval; 1252 | BLOCK_SIGALRM; 1253 | 1254 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1255 | case PROC_PATH_FAILED: 1256 | retval = -ENXIO; 1257 | break; 1258 | case PROC_PATH_ROOT_DIR: 1259 | case PROC_PATH_ROOT_SUBDIR: 1260 | retval = -ENOTSUP; 1261 | break; 1262 | case PROC_PATH_PROXY_DIR: 1263 | retval = get_retval(lchown(real_path, uid, gid)); 1264 | break; 1265 | 1266 | default: 1267 | DEFAULT_CASE_INVALID_ENUM; 1268 | } 1269 | if (mount) 1270 | update_auto_unmount(mount); 1271 | UNBLOCK_SIGALRM; 1272 | return retval; 1273 | } 1274 | 1275 | static int afuse_truncate(const char *path, off_t size) 1276 | { 1277 | char *root_name = alloca(strlen(path)); 1278 | char *real_path = alloca(max_path_out_len(path)); 1279 | mount_list_t *mount; 1280 | int retval; 1281 | BLOCK_SIGALRM; 1282 | 1283 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1284 | case PROC_PATH_FAILED: 1285 | retval = -ENXIO; 1286 | break; 1287 | case PROC_PATH_ROOT_DIR: 1288 | case PROC_PATH_ROOT_SUBDIR: 1289 | retval = -ENOTSUP; 1290 | break; 1291 | case PROC_PATH_PROXY_DIR: 1292 | retval = get_retval(truncate(real_path, size)); 1293 | break; 1294 | 1295 | default: 1296 | DEFAULT_CASE_INVALID_ENUM; 1297 | } 1298 | if (mount) 1299 | update_auto_unmount(mount); 1300 | UNBLOCK_SIGALRM; 1301 | return retval; 1302 | } 1303 | 1304 | static int afuse_utime(const char *path, struct utimbuf *buf) 1305 | { 1306 | char *root_name = alloca(strlen(path)); 1307 | char *real_path = alloca(max_path_out_len(path)); 1308 | mount_list_t *mount; 1309 | int retval; 1310 | BLOCK_SIGALRM; 1311 | 1312 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1313 | case PROC_PATH_FAILED: 1314 | retval = -ENXIO; 1315 | break; 1316 | case PROC_PATH_ROOT_DIR: 1317 | retval = -ENOTSUP; 1318 | break; 1319 | case PROC_PATH_ROOT_SUBDIR: 1320 | if (!mount) { 1321 | retval = -ENOTSUP; 1322 | break; 1323 | } 1324 | case PROC_PATH_PROXY_DIR: 1325 | retval = get_retval(utime(real_path, buf)); 1326 | break; 1327 | 1328 | default: 1329 | DEFAULT_CASE_INVALID_ENUM; 1330 | } 1331 | if (mount) 1332 | update_auto_unmount(mount); 1333 | UNBLOCK_SIGALRM; 1334 | return retval; 1335 | } 1336 | 1337 | static int afuse_open(const char *path, struct fuse_file_info *fi) 1338 | { 1339 | int fd; 1340 | char *root_name = alloca(strlen(path)); 1341 | mount_list_t *mount; 1342 | char *real_path = alloca(max_path_out_len(path)); 1343 | int retval; 1344 | BLOCK_SIGALRM; 1345 | 1346 | switch (process_path(path, real_path, root_name, 1, &mount)) { 1347 | case PROC_PATH_FAILED: 1348 | retval = -ENXIO; 1349 | break; 1350 | case PROC_PATH_ROOT_DIR: 1351 | case PROC_PATH_ROOT_SUBDIR: 1352 | retval = -ENOENT; 1353 | break; 1354 | case PROC_PATH_PROXY_DIR: 1355 | fd = open(real_path, fi->flags); 1356 | if (fd == -1) { 1357 | retval = -errno; 1358 | break; 1359 | } 1360 | 1361 | fi->fh = fd; 1362 | if (mount) 1363 | fd_list_add(&mount->fd_list, fd); 1364 | retval = 0; 1365 | break; 1366 | 1367 | default: 1368 | DEFAULT_CASE_INVALID_ENUM; 1369 | } 1370 | if (mount) 1371 | update_auto_unmount(mount); 1372 | UNBLOCK_SIGALRM; 1373 | return retval; 1374 | } 1375 | 1376 | static int afuse_read(const char *path, char *buf, size_t size, off_t offset, 1377 | struct fuse_file_info *fi) 1378 | { 1379 | int res; 1380 | 1381 | (void)path; 1382 | res = pread(fi->fh, buf, size, offset); 1383 | if (res == -1) 1384 | res = -errno; 1385 | 1386 | return res; 1387 | } 1388 | 1389 | static int afuse_write(const char *path, const char *buf, size_t size, 1390 | off_t offset, struct fuse_file_info *fi) 1391 | { 1392 | int res; 1393 | 1394 | (void)path; 1395 | res = pwrite(fi->fh, buf, size, offset); 1396 | if (res == -1) 1397 | res = -errno; 1398 | 1399 | if (user_options.flush_writes) 1400 | fsync(fi->fh); 1401 | 1402 | return res; 1403 | } 1404 | 1405 | static int afuse_release(const char *path, struct fuse_file_info *fi) 1406 | { 1407 | char *root_name = alloca(strlen(path)); 1408 | mount_list_t *mount; 1409 | int retval; 1410 | BLOCK_SIGALRM; 1411 | 1412 | extract_root_name(path, root_name); 1413 | mount = find_mount(root_name); 1414 | retval = get_retval(close(fi->fh)); 1415 | 1416 | if (mount) { 1417 | fd_list_remove(&mount->fd_list, fi->fh); 1418 | update_auto_unmount(mount); 1419 | } 1420 | 1421 | UNBLOCK_SIGALRM; 1422 | return retval; 1423 | } 1424 | 1425 | static int afuse_fsync(const char *path, int isdatasync, 1426 | struct fuse_file_info *fi) 1427 | { 1428 | int res; 1429 | (void)path; 1430 | 1431 | #ifndef HAVE_FDATASYNC 1432 | (void)isdatasync; 1433 | #else 1434 | if (isdatasync) 1435 | res = fdatasync(fi->fh); 1436 | else 1437 | #endif 1438 | res = fsync(fi->fh); 1439 | return get_retval(res); 1440 | } 1441 | 1442 | #if FUSE_VERSION >= 25 1443 | static int afuse_access(const char *path, int mask) 1444 | { 1445 | char *root_name = alloca(strlen(path)); 1446 | char *real_path = alloca(max_path_out_len(path)); 1447 | mount_list_t *mount; 1448 | int retval; 1449 | BLOCK_SIGALRM; 1450 | 1451 | switch (process_path(path, real_path, root_name, 1, &mount)) { 1452 | case PROC_PATH_FAILED: 1453 | retval = -ENXIO; 1454 | break; 1455 | case PROC_PATH_ROOT_DIR: 1456 | case PROC_PATH_PROXY_DIR: 1457 | retval = get_retval(access(real_path, mask)); 1458 | break; 1459 | case PROC_PATH_ROOT_SUBDIR: 1460 | if (mount) 1461 | retval = get_retval(access(real_path, mask)); 1462 | else 1463 | retval = -EACCES; 1464 | break; 1465 | 1466 | default: 1467 | DEFAULT_CASE_INVALID_ENUM; 1468 | } 1469 | if (mount) 1470 | update_auto_unmount(mount); 1471 | UNBLOCK_SIGALRM; 1472 | return retval; 1473 | } 1474 | 1475 | static int afuse_ftruncate(const char *path, off_t size, 1476 | struct fuse_file_info *fi) 1477 | { 1478 | (void)path; 1479 | return get_retval(ftruncate(fi->fh, size)); 1480 | } 1481 | 1482 | static int afuse_create(const char *path, mode_t mode, 1483 | struct fuse_file_info *fi) 1484 | { 1485 | int fd; 1486 | char *root_name = alloca(strlen(path)); 1487 | char *real_path = alloca(max_path_out_len(path)); 1488 | mount_list_t *mount; 1489 | int retval; 1490 | BLOCK_SIGALRM; 1491 | 1492 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1493 | case PROC_PATH_FAILED: 1494 | retval = -ENXIO; 1495 | break; 1496 | case PROC_PATH_ROOT_DIR: 1497 | case PROC_PATH_ROOT_SUBDIR: 1498 | retval = -ENOTSUP; 1499 | break; 1500 | case PROC_PATH_PROXY_DIR: 1501 | fd = open(real_path, fi->flags, mode); 1502 | if (fd == -1) { 1503 | retval = -errno; 1504 | break; 1505 | } 1506 | fi->fh = fd; 1507 | retval = 0; 1508 | break; 1509 | 1510 | default: 1511 | DEFAULT_CASE_INVALID_ENUM; 1512 | } 1513 | if (mount) 1514 | update_auto_unmount(mount); 1515 | UNBLOCK_SIGALRM; 1516 | return retval; 1517 | } 1518 | 1519 | static int afuse_fgetattr(const char *path, struct stat *stbuf, 1520 | struct fuse_file_info *fi) 1521 | { 1522 | (void)path; 1523 | 1524 | return get_retval(fstat(fi->fh, stbuf)); 1525 | } 1526 | #endif 1527 | 1528 | #if FUSE_VERSION >= 25 1529 | static int afuse_statfs(const char *path, struct statvfs *stbuf) 1530 | #else 1531 | static int afuse_statfs(const char *path, struct statfs *stbuf) 1532 | #endif 1533 | { 1534 | char *root_name = alloca(strlen(path)); 1535 | char *real_path = alloca(max_path_out_len(path)); 1536 | mount_list_t *mount; 1537 | int retval; 1538 | BLOCK_SIGALRM; 1539 | 1540 | switch (process_path(path, real_path, root_name, 1, &mount)) { 1541 | case PROC_PATH_FAILED: 1542 | retval = -ENXIO; 1543 | break; 1544 | 1545 | case PROC_PATH_ROOT_DIR: 1546 | #if FUSE_VERSION >= 25 1547 | stbuf->f_namemax = 0x7fffffff; 1548 | stbuf->f_frsize = 512; 1549 | #else 1550 | stbuf->f_namelen = 0x7fffffff; 1551 | #endif 1552 | stbuf->f_bsize = 1024; 1553 | stbuf->f_blocks = 0; 1554 | stbuf->f_bfree = 0; 1555 | stbuf->f_bavail = 0; 1556 | stbuf->f_files = 0; 1557 | stbuf->f_ffree = 0; 1558 | retval = 0; 1559 | break; 1560 | 1561 | case PROC_PATH_ROOT_SUBDIR: 1562 | if (!mount) { 1563 | retval = -EACCES; 1564 | break; 1565 | } 1566 | case PROC_PATH_PROXY_DIR: 1567 | retval = get_retval(statvfs(real_path, stbuf)); 1568 | break; 1569 | 1570 | default: 1571 | DEFAULT_CASE_INVALID_ENUM; 1572 | } 1573 | if (mount) 1574 | update_auto_unmount(mount); 1575 | UNBLOCK_SIGALRM; 1576 | return retval; 1577 | } 1578 | 1579 | void afuse_destroy(void *p) 1580 | { 1581 | (void)p; /* Unused */ 1582 | shutdown(); 1583 | } 1584 | 1585 | #ifdef HAVE_SETXATTR 1586 | /* xattr operations are optional and can safely be left unimplemented */ 1587 | static int afuse_setxattr(const char *path, const char *name, const char *value, 1588 | size_t size, int flags) 1589 | { 1590 | char *root_name = alloca(strlen(path)); 1591 | char *real_path = alloca(max_path_out_len(path)); 1592 | mount_list_t *mount; 1593 | int retval; 1594 | BLOCK_SIGALRM; 1595 | 1596 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1597 | case PROC_PATH_FAILED: 1598 | retval = -ENXIO; 1599 | break; 1600 | case PROC_PATH_ROOT_DIR: 1601 | retval = -ENOENT; 1602 | break; 1603 | case PROC_PATH_ROOT_SUBDIR: 1604 | if (!mount) { 1605 | retval = -ENOTSUP; 1606 | break; 1607 | } 1608 | case PROC_PATH_PROXY_DIR: 1609 | retval = 1610 | get_retval(lsetxattr(real_path, name, value, size, flags)); 1611 | break; 1612 | 1613 | default: 1614 | DEFAULT_CASE_INVALID_ENUM; 1615 | } 1616 | if (mount) 1617 | update_auto_unmount(mount); 1618 | UNBLOCK_SIGALRM; 1619 | return retval; 1620 | } 1621 | 1622 | static int afuse_getxattr(const char *path, const char *name, char *value, 1623 | size_t size) 1624 | { 1625 | char *root_name = alloca(strlen(path)); 1626 | char *real_path = alloca(max_path_out_len(path)); 1627 | mount_list_t *mount; 1628 | int retval; 1629 | BLOCK_SIGALRM; 1630 | 1631 | switch (process_path(path, real_path, root_name, 1, &mount)) { 1632 | case PROC_PATH_FAILED: 1633 | retval = -ENXIO; 1634 | break; 1635 | case PROC_PATH_ROOT_DIR: 1636 | retval = -ENOTSUP; 1637 | break; 1638 | case PROC_PATH_ROOT_SUBDIR: 1639 | if (!mount) { 1640 | retval = -ENOTSUP; 1641 | break; 1642 | } 1643 | case PROC_PATH_PROXY_DIR: 1644 | retval = get_retval(lgetxattr(real_path, name, value, size)); 1645 | break; 1646 | 1647 | default: 1648 | DEFAULT_CASE_INVALID_ENUM; 1649 | } 1650 | if (mount) 1651 | update_auto_unmount(mount); 1652 | UNBLOCK_SIGALRM; 1653 | return retval; 1654 | } 1655 | 1656 | static int afuse_listxattr(const char *path, char *list, size_t size) 1657 | { 1658 | char *root_name = alloca(strlen(path)); 1659 | char *real_path = alloca(max_path_out_len(path)); 1660 | mount_list_t *mount; 1661 | int retval; 1662 | BLOCK_SIGALRM; 1663 | 1664 | switch (process_path(path, real_path, root_name, 1, &mount)) { 1665 | case PROC_PATH_FAILED: 1666 | retval = -ENXIO; 1667 | break; 1668 | case PROC_PATH_ROOT_DIR: 1669 | retval = -ENOTSUP; 1670 | break; 1671 | case PROC_PATH_ROOT_SUBDIR: 1672 | if (!mount) { 1673 | retval = -ENOTSUP; 1674 | break; 1675 | } 1676 | case PROC_PATH_PROXY_DIR: 1677 | retval = get_retval(llistxattr(real_path, list, size)); 1678 | break; 1679 | 1680 | default: 1681 | DEFAULT_CASE_INVALID_ENUM; 1682 | } 1683 | if (mount) 1684 | update_auto_unmount(mount); 1685 | UNBLOCK_SIGALRM; 1686 | return retval; 1687 | } 1688 | 1689 | static int afuse_removexattr(const char *path, const char *name) 1690 | { 1691 | char *root_name = alloca(strlen(path)); 1692 | char *real_path = alloca(max_path_out_len(path)); 1693 | mount_list_t *mount; 1694 | int retval; 1695 | BLOCK_SIGALRM; 1696 | 1697 | switch (process_path(path, real_path, root_name, 0, &mount)) { 1698 | case PROC_PATH_FAILED: 1699 | retval = -ENXIO; 1700 | break; 1701 | case PROC_PATH_ROOT_DIR: 1702 | retval = -ENOTSUP; 1703 | break; 1704 | case PROC_PATH_ROOT_SUBDIR: 1705 | if (!mount) { 1706 | retval = -ENOTSUP; 1707 | break; 1708 | } 1709 | case PROC_PATH_PROXY_DIR: 1710 | retval = get_retval(lremovexattr(real_path, name)); 1711 | break; 1712 | 1713 | default: 1714 | DEFAULT_CASE_INVALID_ENUM; 1715 | } 1716 | if (mount) 1717 | update_auto_unmount(mount); 1718 | UNBLOCK_SIGALRM; 1719 | return retval; 1720 | } 1721 | #endif /* HAVE_SETXATTR */ 1722 | 1723 | static struct fuse_operations afuse_oper = { 1724 | .getattr = afuse_getattr, 1725 | .readlink = afuse_readlink, 1726 | .opendir = afuse_opendir, 1727 | .readdir = afuse_readdir, 1728 | .releasedir = afuse_releasedir, 1729 | .mknod = afuse_mknod, 1730 | .mkdir = afuse_mkdir, 1731 | .symlink = afuse_symlink, 1732 | .unlink = afuse_unlink, 1733 | .rmdir = afuse_rmdir, 1734 | .rename = afuse_rename, 1735 | .link = afuse_link, 1736 | .chmod = afuse_chmod, 1737 | .chown = afuse_chown, 1738 | .truncate = afuse_truncate, 1739 | .utime = afuse_utime, 1740 | .open = afuse_open, 1741 | .read = afuse_read, 1742 | .write = afuse_write, 1743 | .release = afuse_release, 1744 | .fsync = afuse_fsync, 1745 | .statfs = afuse_statfs, 1746 | #if FUSE_VERSION >= 25 1747 | .access = afuse_access, 1748 | .create = afuse_create, 1749 | .ftruncate = afuse_ftruncate, 1750 | .fgetattr = afuse_fgetattr, 1751 | #endif 1752 | .destroy = afuse_destroy, 1753 | #ifdef HAVE_SETXATTR 1754 | .setxattr = afuse_setxattr, 1755 | .getxattr = afuse_getxattr, 1756 | .listxattr = afuse_listxattr, 1757 | .removexattr = afuse_removexattr, 1758 | #endif 1759 | }; 1760 | 1761 | enum { 1762 | KEY_HELP, 1763 | KEY_FLUSHWRITES, 1764 | KEY_EXACT_GETATTR 1765 | }; 1766 | 1767 | #define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v } 1768 | 1769 | static struct fuse_opt afuse_opts[] = { 1770 | AFUSE_OPT("mount_template=%s", mount_command_template, 0), 1771 | AFUSE_OPT("unmount_template=%s", unmount_command_template, 0), 1772 | AFUSE_OPT("populate_root_command=%s", populate_root_command, 0), 1773 | AFUSE_OPT("filter_file=%s", filter_file, 0), 1774 | AFUSE_OPT("mount_dir=%s", mount_dir, 0), 1775 | 1776 | AFUSE_OPT("timeout=%llu", auto_unmount_delay, 0), 1777 | 1778 | FUSE_OPT_KEY("exact_getattr", KEY_EXACT_GETATTR), 1779 | FUSE_OPT_KEY("flushwrites", KEY_FLUSHWRITES), 1780 | FUSE_OPT_KEY("-h", KEY_HELP), 1781 | FUSE_OPT_KEY("--help", KEY_HELP), 1782 | 1783 | FUSE_OPT_END 1784 | }; 1785 | 1786 | static void usage(const char *progname) 1787 | { 1788 | fprintf(stderr, 1789 | "Usage: %s mountpoint [options]\n" 1790 | "\n" 1791 | " -o opt,[opt...] mount options\n" 1792 | " -h --help print help\n" 1793 | " -V --version print FUSE version information\n" 1794 | "\n" 1795 | "afuse options:\n" 1796 | " -o mount_template=CMD template for CMD to execute to mount (1)\n" 1797 | " -o unmount_template=CMD template for CMD to execute to unmount (1) (2)\n" 1798 | " -o populate_root_command=CMD CMD to execute providing root directory list (3)\n" 1799 | " -o filter_file=FILE FILE listing ignore filters for mount points (4)\n" 1800 | " -o timeout=TIMEOUT automatically unmount after TIMEOUT seconds\n" 1801 | " -o flushwrites flushes data to disk for all file writes\n" 1802 | " -o exact_getattr allows getattr calls to cause a mount\n" 1803 | " -o mount_dir=DIR place temporary mounts under DIR (default: /tmp)\n" 1804 | "\n\n" 1805 | " (1) - When executed, %%r is expanded to the directory name inside the\n" 1806 | " afuse mount, and %%m is expanded to the actual directory to mount\n" 1807 | " onto. Both templates are REQUIRED.\n" 1808 | "\n" 1809 | " (2) - The unmount command must perform a lazy unmount operation. E.g. the\n" 1810 | " -u -z options to fusermount, or -l for regular mount.\n" 1811 | "\n" 1812 | " (3) - The populate_root command should output one dir entry per line,\n" 1813 | " and return immediately. It is run for each directory listing request.\n" 1814 | "\n" 1815 | " (4) - Each line of the filter file is a shell wildcard filter (glob). A '#'\n" 1816 | " as the first character on a line ignores a filter.\n" 1817 | "\n" 1818 | " The following filter patterns are hard-coded:" 1819 | "\n", progname); 1820 | 1821 | mount_filter_list_t *cur = mount_filter_list; 1822 | while (cur) { 1823 | fprintf(stderr, " %s\n", cur->pattern); 1824 | cur = cur->next; 1825 | } 1826 | 1827 | fprintf(stderr, "\n"); 1828 | } 1829 | 1830 | static int afuse_opt_proc(void *data, const char *arg, int key, 1831 | struct fuse_args *outargs) 1832 | { 1833 | /* Unused */ 1834 | (void)arg; 1835 | (void)data; 1836 | 1837 | switch (key) { 1838 | case KEY_HELP: 1839 | usage(outargs->argv[0]); 1840 | fuse_opt_add_arg(outargs, "-ho"); 1841 | fuse_main(outargs->argc, outargs->argv, &afuse_oper); 1842 | exit(1); 1843 | 1844 | case KEY_FLUSHWRITES: 1845 | user_options.flush_writes = true; 1846 | return 0; 1847 | 1848 | case KEY_EXACT_GETATTR: 1849 | user_options.exact_getattr = true; 1850 | return 0; 1851 | 1852 | default: 1853 | return 1; 1854 | } 1855 | } 1856 | 1857 | int main(int argc, char *argv[]) 1858 | { 1859 | char *temp_dir_name; 1860 | struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 1861 | 1862 | if (fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == 1863 | -1) 1864 | return 1; 1865 | 1866 | // !!FIXME!! force single-threading for now as data structures are not locked 1867 | fuse_opt_add_arg(&args, "-s"); 1868 | 1869 | // Adjust user specified timeout from seconds to microseconds as required 1870 | if (user_options.auto_unmount_delay != UINT64_MAX) 1871 | user_options.auto_unmount_delay *= 1000000; 1872 | 1873 | auto_unmount_ph_init(&auto_unmount_ph); 1874 | 1875 | /** 1876 | * Install the SIGALRM signal handler. 1877 | */ 1878 | { 1879 | struct sigaction act; 1880 | act.sa_handler = &handle_auto_unmount_timer; 1881 | sigemptyset(&act.sa_mask); 1882 | act.sa_flags = 0; 1883 | while (sigaction(SIGALRM, &act, NULL) == -1 && errno == EINTR) 1884 | continue; 1885 | } 1886 | 1887 | if (!user_options.mount_dir) { 1888 | size_t buflen = strlen(TMP_DIR_TEMPLATE); 1889 | temp_dir_name = my_malloc(buflen+1); 1890 | temp_dir_name[buflen] = '\0'; 1891 | strncpy(temp_dir_name, TMP_DIR_TEMPLATE, buflen); 1892 | } else { 1893 | size_t buflen1 = strlen(user_options.mount_dir); 1894 | size_t buflen2 = strlen(TMP_DIR_TEMPLATE2); 1895 | temp_dir_name = my_malloc(buflen1+buflen2+1); 1896 | temp_dir_name[buflen1+buflen2] = '\0'; 1897 | strncpy(temp_dir_name, user_options.mount_dir, buflen1); 1898 | strncpy(temp_dir_name + strlen(user_options.mount_dir), 1899 | TMP_DIR_TEMPLATE2, buflen2); 1900 | } 1901 | 1902 | // Check for required parameters 1903 | if (!user_options.mount_command_template 1904 | || !user_options.unmount_command_template) { 1905 | fprintf(stderr, "(Un)Mount command templates missing.\n\n"); 1906 | usage(argv[0]); 1907 | fuse_opt_add_arg(&args, "-ho"); 1908 | fuse_main(args.argc, args.argv, &afuse_oper); 1909 | 1910 | return 1; 1911 | } 1912 | 1913 | if (user_options.filter_file) 1914 | load_mount_filter_file(user_options.filter_file); 1915 | 1916 | if (!(mount_point_directory = mkdtemp(temp_dir_name))) { 1917 | fprintf(stderr, 1918 | "Failed to create temporary mount point dir.\n"); 1919 | return 1; 1920 | } 1921 | 1922 | { 1923 | struct stat buf; 1924 | if (lstat(mount_point_directory, &buf) == -1) { 1925 | fprintf(stderr, 1926 | "Failed to stat temporary mount point dir.\n"); 1927 | return 1; 1928 | } 1929 | mount_point_dev = buf.st_dev; 1930 | } 1931 | 1932 | umask(0); 1933 | 1934 | // !!FIXME!! death by signal doesn't unmount fs 1935 | return fuse_main(args.argc, args.argv, &afuse_oper); 1936 | } 1937 | -------------------------------------------------------------------------------- /src/afuse.h: -------------------------------------------------------------------------------- 1 | #ifndef __AFUSE_H 2 | #define __AFUSE_H 3 | 4 | // Global definitions in use through afuse source 5 | 6 | // When closing an fd/dir, the close may fail due to a signal 7 | // this value defines how many times we retry in this case. 8 | // It's useful to try and close as many fd's as possible 9 | // for the proxied fs to increase the chance an umount will 10 | // succeed. 11 | #define CLOSE_MAX_RETRIES 5 12 | 13 | #endif // __AFUSE_H 14 | -------------------------------------------------------------------------------- /src/dir_list.c: -------------------------------------------------------------------------------- 1 | #define __DIR_LIST_C 2 | 3 | #include 4 | #include 5 | #include "afuse.h" 6 | #include "utils.h" 7 | #include "dir_list.h" 8 | 9 | void dir_list_add(dir_list_t ** dir_list, DIR * dir) 10 | { 11 | dir_list_t *new_dir; 12 | 13 | new_dir = my_malloc(sizeof(dir_list_t)); 14 | new_dir->dir = dir; 15 | new_dir->next = *dir_list; 16 | new_dir->prev = NULL; 17 | 18 | *dir_list = new_dir; 19 | } 20 | 21 | void dir_list_remove(dir_list_t ** dir_list, DIR * dir) 22 | { 23 | dir_list_t *current_dir = *dir_list; 24 | 25 | while (current_dir) { 26 | if (current_dir->dir == dir) { 27 | if (current_dir->prev) 28 | current_dir->prev->next = current_dir->next; 29 | else 30 | *dir_list = current_dir->next; 31 | if (current_dir->next) 32 | current_dir->next->prev = current_dir->prev; 33 | free(current_dir); 34 | 35 | return; 36 | } 37 | 38 | current_dir = current_dir->next; 39 | } 40 | } 41 | 42 | void dir_list_close_all(dir_list_t ** dir_list) 43 | { 44 | while (*dir_list) { 45 | int retries; 46 | 47 | for (retries = 0; retries < CLOSE_MAX_RETRIES && 48 | closedir((*dir_list)->dir) == -1 && 49 | errno == EINTR; retries++) ; 50 | dir_list_remove(dir_list, (*dir_list)->dir); 51 | } 52 | } 53 | 54 | bool dir_list_empty(dir_list_t * dir_list) 55 | { 56 | return (dir_list == NULL) ? true : false; 57 | } 58 | -------------------------------------------------------------------------------- /src/dir_list.h: -------------------------------------------------------------------------------- 1 | #ifndef __DIR_LIST_H 2 | #define __DIR_LIST_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct _dir_list_t { 8 | struct _dir_list_t *next; 9 | struct _dir_list_t *prev; 10 | 11 | DIR *dir; 12 | } dir_list_t; 13 | 14 | #undef EXTERN 15 | #ifdef __DIR_LIST_C 16 | #define EXTERN 17 | #else 18 | #define EXTERN extern 19 | #endif 20 | 21 | EXTERN void dir_list_add(dir_list_t ** dir_list, DIR * dir); 22 | EXTERN void dir_list_remove(dir_list_t ** dir_list, DIR * dir); 23 | EXTERN void dir_list_close_all(dir_list_t ** dir_list); 24 | EXTERN bool dir_list_empty(dir_list_t * dir_list); 25 | 26 | #endif // __DIR_LIST_H 27 | -------------------------------------------------------------------------------- /src/fd_list.c: -------------------------------------------------------------------------------- 1 | #define __FD_LIST_C 2 | 3 | #include 4 | #include 5 | #include "afuse.h" 6 | #include "utils.h" 7 | #include "fd_list.h" 8 | 9 | void fd_list_add(fd_list_t ** fd_list, int fd) 10 | { 11 | fd_list_t *new_fd; 12 | 13 | new_fd = my_malloc(sizeof(fd_list_t)); 14 | new_fd->fd = fd; 15 | new_fd->next = *fd_list; 16 | new_fd->prev = NULL; 17 | 18 | *fd_list = new_fd; 19 | } 20 | 21 | void fd_list_remove(fd_list_t ** fd_list, int fd) 22 | { 23 | fd_list_t *current_fd = *fd_list; 24 | 25 | while (current_fd) { 26 | if (current_fd->fd == fd) { 27 | if (current_fd->prev) 28 | current_fd->prev->next = current_fd->next; 29 | else 30 | *fd_list = current_fd->next; 31 | if (current_fd->next) 32 | current_fd->next->prev = current_fd->prev; 33 | free(current_fd); 34 | 35 | return; 36 | } 37 | 38 | current_fd = current_fd->next; 39 | } 40 | } 41 | 42 | void fd_list_close_all(fd_list_t ** fd_list) 43 | { 44 | while (*fd_list) { 45 | int retries; 46 | 47 | for (retries = 0; retries < CLOSE_MAX_RETRIES && 48 | close((*fd_list)->fd) == -1 && errno == EINTR; retries++) ; 49 | fd_list_remove(fd_list, (*fd_list)->fd); 50 | } 51 | } 52 | 53 | bool fd_list_empty(fd_list_t * fd_list) 54 | { 55 | return (fd_list == NULL) ? true : false; 56 | } 57 | -------------------------------------------------------------------------------- /src/fd_list.h: -------------------------------------------------------------------------------- 1 | #ifndef __FD_LIST_H 2 | #define __FD_LIST_H 3 | 4 | #include 5 | 6 | // Link list holding open file descriptors associated with a mount 7 | 8 | typedef struct _fd_list_t { 9 | struct _fd_list_t *next; 10 | struct _fd_list_t *prev; 11 | 12 | int fd; 13 | } fd_list_t; 14 | 15 | #undef EXTERN 16 | #ifdef __DIR_LIST_C 17 | #define EXTERN 18 | #else 19 | #define EXTERN extern 20 | #endif 21 | 22 | EXTERN void fd_list_add(fd_list_t ** fd_list, int fd); 23 | EXTERN void fd_list_remove(fd_list_t ** fd_list, int fd); 24 | EXTERN bool fd_list_empty(fd_list_t * fd_list); 25 | EXTERN void fd_list_close_all(fd_list_t ** fd_list); 26 | 27 | #endif // __FD_LIST_H 28 | -------------------------------------------------------------------------------- /src/string_sorted_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "string_sorted_list.h" 7 | 8 | struct list_t { 9 | struct list_t *prev, *next; 10 | char *name; 11 | }; 12 | 13 | static char *stralloccopy(char const *const str) 14 | { 15 | char *s = malloc((strlen(str) + 1) * sizeof(*s)); 16 | if (s != NULL) 17 | strcpy(s, str); 18 | return s; 19 | } 20 | 21 | static struct list_t *new_list_element(struct list_t *prev, struct list_t *next, 22 | char const *const name) 23 | { 24 | struct list_t *e; 25 | e = malloc(sizeof(*e)); 26 | if (!e) { 27 | return NULL; 28 | } 29 | e->prev = prev; 30 | e->next = next; 31 | e->name = stralloccopy(name); 32 | if (!e->name) { 33 | free(e); 34 | return NULL; 35 | } 36 | return e; 37 | } 38 | 39 | static struct list_t *insert_between(struct list_t *prev, struct list_t *next, 40 | char const *const name) 41 | { 42 | struct list_t *e; 43 | 44 | e = new_list_element(prev, next, name); 45 | if (e == NULL) 46 | return NULL; 47 | 48 | if (prev) 49 | prev->next = e; 50 | if (next) 51 | next->prev = e; 52 | 53 | return e; 54 | } 55 | 56 | static int update_list(struct list_t **list, struct list_t *prev, 57 | struct list_t *next, char const *const name) 58 | { 59 | struct list_t *e; 60 | 61 | e = insert_between(prev, next, name); 62 | if (e == NULL) 63 | return -1; 64 | 65 | *list = e; 66 | 67 | return 0; 68 | } 69 | 70 | int insert_sorted_if_unique(struct list_t **list, char const *const name) 71 | { 72 | struct list_t *prev, *curr, *next; 73 | int cmp; 74 | 75 | if (*list == NULL) 76 | return update_list(list, NULL, NULL, name); 77 | 78 | curr = NULL; 79 | next = *list; 80 | do { 81 | prev = curr; 82 | curr = next; 83 | cmp = strcmp(name, curr->name); 84 | if (cmp == 0) 85 | return 1; 86 | else if (cmp > 0) 87 | next = curr->next; 88 | else 89 | next = curr->prev; 90 | } while (next && next != prev); 91 | 92 | if (cmp > 0) 93 | return update_list(list, curr, curr->next, name); 94 | else 95 | return update_list(list, curr->prev, curr, name); 96 | } 97 | 98 | void destroy_list(struct list_t **list) 99 | { 100 | struct list_t *curr, *next; 101 | 102 | if (*list == NULL) 103 | return; 104 | 105 | curr = (*list)->next; 106 | while (curr) { 107 | next = curr->next; 108 | free(curr); 109 | curr = next; 110 | } 111 | 112 | curr = (*list); 113 | while (curr) { 114 | next = curr->prev; 115 | free(curr); 116 | curr = next; 117 | } 118 | 119 | *list = NULL; 120 | } 121 | 122 | void print_list(struct list_t const *curr) 123 | { 124 | if (curr == NULL) { 125 | fprintf(stderr, "list is empty\n"); 126 | return; 127 | } 128 | 129 | fprintf(stderr, "Sorted list: "); 130 | 131 | /* go to beginning */ 132 | while (curr->prev) 133 | curr = curr->prev; 134 | 135 | do { 136 | fprintf(stderr, " %s,", curr->name); 137 | curr = curr->next; 138 | } while (curr); 139 | fprintf(stderr, "\n"); 140 | } 141 | -------------------------------------------------------------------------------- /src/string_sorted_list.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_SORTED_LIST_H 2 | #define STRING_SORTED_LIST_H 3 | 4 | struct list_t; 5 | 6 | int insert_sorted_if_unique(struct list_t **list, char const *const name); 7 | 8 | void destroy_list(struct list_t **list); 9 | 10 | void print_list(struct list_t const *curr); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #define __UTILS_C 2 | 3 | #include 4 | #include 5 | #include 6 | #include "utils.h" 7 | 8 | void *my_malloc(size_t size) 9 | { 10 | void *p; 11 | 12 | p = malloc(size); 13 | 14 | if (!p) { 15 | fprintf(stderr, "Failed to allocate: %zu bytes of memory.\n", 16 | size); 17 | exit(1); 18 | } 19 | 20 | return p; 21 | } 22 | 23 | char *my_strdup(const char *str) 24 | { 25 | char *new_str; 26 | 27 | new_str = my_malloc(strlen(str) + 1); 28 | strcpy(new_str, str); 29 | 30 | return new_str; 31 | } 32 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H 2 | #define __UTILS_H 3 | 4 | #include 5 | 6 | #undef EXTERN 7 | #ifdef __UTILS_C 8 | #define EXTERN 9 | #else 10 | #define EXTERN extern 11 | #endif 12 | 13 | EXTERN void *my_malloc(size_t size); 14 | EXTERN char *my_strdup(const char *str); 15 | 16 | #endif // __UTILS_H 17 | -------------------------------------------------------------------------------- /src/variable_pairing_heap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file variable_pairing_heap.h 3 | * 4 | * @author Jeremy Maitin-Shepard 5 | * 6 | * This file defines a type-generic pairing heap implementation as 7 | * several macros that generate the implementation code. 8 | */ 9 | 10 | #ifndef _VARIABLE_PAIRING_HEAP_H 11 | #define _VARIABLE_PAIRING_HEAP_H 12 | 13 | #define PH_NEW_LINK(PH_ELEM_TYPE) \ 14 | struct \ 15 | { \ 16 | PH_ELEM_TYPE *child, *next, *prev; \ 17 | } 18 | 19 | #define PH_DECLARE_TYPE(PH_PREFIX, PH_ELEM_TYPE) \ 20 | typedef PH_ELEM_TYPE *PH_PREFIX ## _t; \ 21 | void PH_PREFIX ## _init(PH_PREFIX ## _t *ph); \ 22 | PH_ELEM_TYPE *PH_PREFIX ## _min(PH_PREFIX ## _t *ph); \ 23 | void PH_PREFIX ## _insert(PH_PREFIX ## _t *ph, PH_ELEM_TYPE *elem); \ 24 | void PH_PREFIX ## _remove(PH_PREFIX ## _t *ph, PH_ELEM_TYPE *elem); \ 25 | void PH_PREFIX ## _remove_min(PH_PREFIX ## _t *ph); 26 | 27 | #define PH_DEFINE_TYPE(PH_PREFIX, PH_ELEM_TYPE, PH_NODE_NAME, PH_KEY_NAME) \ 28 | void PH_PREFIX ## _init(PH_PREFIX ## _t *ph) \ 29 | { \ 30 | *ph = NULL; \ 31 | } \ 32 | PH_ELEM_TYPE *PH_PREFIX ## _min(PH_PREFIX ## _t *ph) \ 33 | { \ 34 | return *ph; \ 35 | } \ 36 | static PH_ELEM_TYPE *PH_PREFIX ## _meld(PH_ELEM_TYPE *a, \ 37 | PH_ELEM_TYPE *b) \ 38 | { \ 39 | if (!b) \ 40 | return a; \ 41 | if (a->PH_KEY_NAME > b->PH_KEY_NAME) \ 42 | { \ 43 | PH_ELEM_TYPE *temp = a; \ 44 | a = b; \ 45 | b = temp; \ 46 | } \ 47 | b->PH_NODE_NAME.next = a->PH_NODE_NAME.child; \ 48 | b->PH_NODE_NAME.prev = a; \ 49 | if (a->PH_NODE_NAME.child) \ 50 | a->PH_NODE_NAME.child->PH_NODE_NAME.prev = b; \ 51 | a->PH_NODE_NAME.child = b; \ 52 | return a; \ 53 | } \ 54 | void PH_PREFIX ## _insert(PH_PREFIX ## _t *ph, PH_ELEM_TYPE *elem) \ 55 | { \ 56 | elem->PH_NODE_NAME.child = NULL; \ 57 | elem->PH_NODE_NAME.next = NULL; \ 58 | elem->PH_NODE_NAME.prev = NULL; \ 59 | *ph = PH_PREFIX ## _meld(elem, *ph); \ 60 | } \ 61 | static PH_ELEM_TYPE *PH_PREFIX ## _combine_children(PH_ELEM_TYPE *ph) \ 62 | { \ 63 | PH_ELEM_TYPE *head = NULL, *tail = NULL, *cur = ph->PH_NODE_NAME.child, \ 64 | *next, *nnext, *m = NULL; \ 65 | if (!cur) \ 66 | return NULL; \ 67 | while (1) \ 68 | { \ 69 | if (!cur->PH_NODE_NAME.next) \ 70 | next = NULL; \ 71 | else \ 72 | next = cur->PH_NODE_NAME.next->PH_NODE_NAME.next; \ 73 | m = PH_PREFIX ## _meld(cur, cur->PH_NODE_NAME.next); \ 74 | if (tail) \ 75 | tail->PH_NODE_NAME.next = m; \ 76 | else \ 77 | head = m; \ 78 | tail = m; \ 79 | if (!next) \ 80 | break; \ 81 | cur = next; \ 82 | } \ 83 | while (head != tail) \ 84 | { \ 85 | next = head->PH_NODE_NAME.next; \ 86 | nnext = next->PH_NODE_NAME.next; \ 87 | m = PH_PREFIX ## _meld(head, next); \ 88 | if (next == tail) \ 89 | break; \ 90 | tail->PH_NODE_NAME.next = m; \ 91 | tail = m; \ 92 | head = nnext; \ 93 | } \ 94 | m->PH_NODE_NAME.prev = NULL; \ 95 | m->PH_NODE_NAME.next = NULL; \ 96 | return m; \ 97 | } \ 98 | void PH_PREFIX ## _remove_min(PH_PREFIX ## _t *ph) \ 99 | { \ 100 | *ph = PH_PREFIX ## _combine_children(*ph); \ 101 | } \ 102 | void PH_PREFIX ## _remove(PH_PREFIX ## _t *ph, PH_ELEM_TYPE *elem) \ 103 | { \ 104 | if (elem == *ph) \ 105 | PH_PREFIX ## _remove_min(ph); \ 106 | else \ 107 | { \ 108 | PH_ELEM_TYPE *prev = elem->PH_NODE_NAME.prev; \ 109 | if (prev->PH_NODE_NAME.child == elem) \ 110 | prev->PH_NODE_NAME.child = elem->PH_NODE_NAME.next; \ 111 | else \ 112 | prev->PH_NODE_NAME.next = elem->PH_NODE_NAME.next; \ 113 | if (elem->PH_NODE_NAME.next) \ 114 | elem->PH_NODE_NAME.next->PH_NODE_NAME.prev = prev; \ 115 | *ph = PH_PREFIX ## _meld \ 116 | (*ph, PH_PREFIX ## _combine_children(elem)); \ 117 | } \ 118 | } 119 | 120 | #endif /* _VARIABLE_PAIRING_HEAP_H */ 121 | --------------------------------------------------------------------------------