pax_global_header00006660000000000000000000000064152135347160014521gustar00rootroot0000000000000052 comment=1014fee00e2c4d525d333a973c7d4f5916e8dba6 fuseiso-v20260614/000077500000000000000000000000001521353471600136325ustar00rootroot00000000000000fuseiso-v20260614/.builds/000077500000000000000000000000001521353471600151725ustar00rootroot00000000000000fuseiso-v20260614/.builds/alpine-linux.yml000066400000000000000000000006101521353471600203170ustar00rootroot00000000000000# Copyright 2026 Hugo Osvaldo Barrera # # SPDX-License-Identifier: GPL-2.0-only image: alpine/edge packages: - fuse3-dev - glib-dev - meson sources: - https://git.sr.ht/~whynothugo/fuseiso triggers: - action: email condition: failure to: Hugo Osvaldo Barrera tasks: - build: | cd fuseiso abuild-meson . build meson compile -C build fuseiso-v20260614/AUTHORS000066400000000000000000000003161521353471600147020ustar00rootroot00000000000000Dmitry Morozhnikov Patches by: Ryan Thomas Chandan Dutta Chowdhury headers & ideas borrowed from linux kernel isofs code fuseiso-v20260614/COPYING000066400000000000000000000431311521353471600146670ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. fuseiso-v20260614/ChangeLog000066400000000000000000000037511521353471600154120ustar00rootroot000000000000002026-06-14 * all: Fix iconv errors on non-glibc. * all: Print a more detailed message on too long path. * all: Fix infinite loop parsing line options on arm. * all: Switch to fuse3. * all: Report correct size of symlinks. * all: Recognize fuse options placed before the arguments. * all: Correct calculation of block count 2026-05-07 * all: Fix integer overflow. CVE-2015-8836 * all: Fix buffer overflow. CVE-2015-8837 * all: Fix several other overflow and wrapping errors. 2007-07-08 * all: Patch by Ryan Thomas to fix race condition with ZISO files. Patch by Chandan Dutta Chowdhury to fix compilation on RHEL3/RHEL4. Thanks! 2006-10-17 * all: Fixed crash related to handling wrong-coded names with Juliet extension. 2006-09-28 * all: Fixed race-condition with find -- display stable inode numbers on mounted filesystem to the outside world. Added check/addition for library option "use_ino" -- required from now. isofs_real_init() split into two parts to make real initialization/error checking before giving control to libfuse. 2006-09-27 * all: Several options added to maintain mount point (create if it does not exists, delete on exit), do NOT maintain file ~/.mtab.fuseiso (with format of /etc/mtab), specify iocharset (default iocharset is now locale-specific). 2006-09-26 * all: Added support to mounting NRG-image files created by Nero software. 2006-09-25 * all: Added support to mounting BIN-image files. One-track only for now. 2006-09-23 * all: Fixed directory read code to take directory end and block borders more correctly. 2006-07-31 * all: Fixed casting bug in isofs_parse_zisofs_header(). Parse SL RR entries with flag 3 as directory separator. 2006-01-07 * all: Fixed problem with current directory after call to daemon() in libfuse. Thanks a lot to Jonathan Brandmeyer for bug hunting and provided patch. fuseiso-v20260614/INSTALL000066400000000000000000000156711521353471600146750ustar00rootroot00000000000000NOTE by Alexey Eremenko: if you're running rpm-based distro, most chances that you will need "fuse-devel" package installed. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Type `make install' to install the programs and any data files and documentation. 4. You can remove the program binaries and object files from the source code directory by typing `make clean'. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. fuseiso-v20260614/NEWS000066400000000000000000000004271521353471600143340ustar00rootroot000000000000002006-09-27 * Added support to maintain ~/.mtab.fuseiso, maintain mount point directory. Added support for iocharset. 2006-09-26 * Added support to mounting NRG-image files. 2006-09-25 * Added support to mounting BIN-image files. One-track only for now. fuseiso-v20260614/README000066400000000000000000000035131521353471600145140ustar00rootroot00000000000000Usage: fuseiso [] [] mounts image, while fusermount shipped with FUSE library can be used to unmount: fusermount -u fuseiso options are: -p Maintain mountpoint. Create it if it does not exists on start, delete it on exit. -n Do NOT maintain ~/.mtab.fuseiso . This file have format of /etc/mtab and normally stores information about currently mounted iso images. -c iocharset Specify iocharset to use. Joliet filesystem store filenames in unicode and to show them properly they need to be converted to local charset. Default charset is a current locale charset. fuseiso supports plain ISO images (created by mkisofs for example), BIN and NRG images containing ISO9660 filesystem. Along with standard ISO9660 filesystem it support some common extensions: Joliet Common in windows world. Allow long filenames stored in unicode. RockRidge Common in unix world. Allow long filenames, deep directories, symbolic links and permission bits to be stored. zisofs Compressed filesystem, drastically increases capasity of standard CDROM. In fact i found what CCD (CloneCD) .IMG files along with .MDF (Alcohol 120%) images can be mounted without problems because their format looks exactly as .BIN image file format. So currently fuseiso supports disk images with following extensions: .iso .img .bin .mdf .nrg Although, BIN images support have now major limitation -- fuseiso does not handle .CUE files in any way and thus can work only with first track of the BIN image. I don`t know if this is important to support .CUE files properly. Please email me if you need it. Support for other types of media descriptors like .ccd and .mds looks more difficult task because no one know it`s format. fuseiso-v20260614/TODO000066400000000000000000000000001521353471600143100ustar00rootroot00000000000000fuseiso-v20260614/linux/000077500000000000000000000000001521353471600147715ustar00rootroot00000000000000fuseiso-v20260614/linux/iso_fs.h000066400000000000000000000277361521353471600164430ustar00rootroot00000000000000// this file was borrowed from linux kernel // originally it has name /usr/src/linux/include/linux/iso_fs.h // unfortunately there was no copyright header on it, // so i`m assume it have copyrighted under terms of // GNU GENERAL PUBLIC LICENSE version 2 // as a whole linux kernel // copy of this license included into fuseiso // distribution in file COPYING #ifndef _ISOFS_FS_H #define _ISOFS_FS_H #include /* * The isofs filesystem constants/structures */ /* This part borrowed from the bsd386 isofs */ #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* volume descriptor types */ #define ISO_VD_PRIMARY 1 #define ISO_VD_SUPPLEMENTARY 2 #define ISO_VD_END 255 #define ISO_STANDARD_ID "CD001" struct iso_primary_descriptor { char type [ISODCL ( 1, 1)]; /* 711 */ char id [ISODCL ( 2, 6)]; char version [ISODCL ( 7, 7)]; /* 711 */ char unused1 [ISODCL ( 8, 8)]; char system_id [ISODCL ( 9, 40)]; /* achars */ char volume_id [ISODCL ( 41, 72)]; /* dchars */ char unused2 [ISODCL ( 73, 80)]; char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ char unused3 [ISODCL ( 89, 120)]; char volume_set_size [ISODCL (121, 124)]; /* 723 */ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ char logical_block_size [ISODCL (129, 132)]; /* 723 */ char path_table_size [ISODCL (133, 140)]; /* 733 */ char type_l_path_table [ISODCL (141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ char type_m_path_table [ISODCL (149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ char volume_set_id [ISODCL (191, 318)]; /* dchars */ char publisher_id [ISODCL (319, 446)]; /* achars */ char preparer_id [ISODCL (447, 574)]; /* achars */ char application_id [ISODCL (575, 702)]; /* achars */ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL (882, 882)]; /* 711 */ char unused4 [ISODCL (883, 883)]; char application_data [ISODCL (884, 1395)]; char unused5 [ISODCL (1396, 2048)]; }; /* Almost the same as the primary descriptor but two fields are specified */ struct iso_supplementary_descriptor { char type [ISODCL ( 1, 1)]; /* 711 */ char id [ISODCL ( 2, 6)]; char version [ISODCL ( 7, 7)]; /* 711 */ char flags [ISODCL ( 8, 8)]; /* 853 */ char system_id [ISODCL ( 9, 40)]; /* achars */ char volume_id [ISODCL ( 41, 72)]; /* dchars */ char unused2 [ISODCL ( 73, 80)]; char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ char escape [ISODCL ( 89, 120)]; /* 856 */ char volume_set_size [ISODCL (121, 124)]; /* 723 */ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ char logical_block_size [ISODCL (129, 132)]; /* 723 */ char path_table_size [ISODCL (133, 140)]; /* 733 */ char type_l_path_table [ISODCL (141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ char type_m_path_table [ISODCL (149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ char volume_set_id [ISODCL (191, 318)]; /* dchars */ char publisher_id [ISODCL (319, 446)]; /* achars */ char preparer_id [ISODCL (447, 574)]; /* achars */ char application_id [ISODCL (575, 702)]; /* achars */ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL (882, 882)]; /* 711 */ char unused4 [ISODCL (883, 883)]; char application_data [ISODCL (884, 1395)]; char unused5 [ISODCL (1396, 2048)]; }; #define HS_STANDARD_ID "CDROM" struct hs_volume_descriptor { char foo [ISODCL ( 1, 8)]; /* 733 */ char type [ISODCL ( 9, 9)]; /* 711 */ char id [ISODCL ( 10, 14)]; char version [ISODCL ( 15, 15)]; /* 711 */ char data[ISODCL(16,2048)]; }; struct hs_primary_descriptor { char foo [ISODCL ( 1, 8)]; /* 733 */ char type [ISODCL ( 9, 9)]; /* 711 */ char id [ISODCL ( 10, 14)]; char version [ISODCL ( 15, 15)]; /* 711 */ char unused1 [ISODCL ( 16, 16)]; /* 711 */ char system_id [ISODCL ( 17, 48)]; /* achars */ char volume_id [ISODCL ( 49, 80)]; /* dchars */ char unused2 [ISODCL ( 81, 88)]; /* 733 */ char volume_space_size [ISODCL ( 89, 96)]; /* 733 */ char unused3 [ISODCL ( 97, 128)]; /* 733 */ char volume_set_size [ISODCL (129, 132)]; /* 723 */ char volume_sequence_number [ISODCL (133, 136)]; /* 723 */ char logical_block_size [ISODCL (137, 140)]; /* 723 */ char path_table_size [ISODCL (141, 148)]; /* 733 */ char type_l_path_table [ISODCL (149, 152)]; /* 731 */ char unused4 [ISODCL (153, 180)]; /* 733 */ char root_directory_record [ISODCL (181, 214)]; /* 9.1 */ }; /* We use this to help us look up the parent inode numbers. */ struct iso_path_table{ unsigned char name_len[2]; /* 721 */ char extent[4]; /* 731 */ char parent[2]; /* 721 */ char name[0]; } __attribute__((packed)); /* high sierra is identical to iso, except that the date is only 6 bytes, and there is an extra reserved byte after the flags */ struct iso_directory_record { char length [ISODCL (1, 1)]; /* 711 */ char ext_attr_length [ISODCL (2, 2)]; /* 711 */ char extent [ISODCL (3, 10)]; /* 733 */ char size [ISODCL (11, 18)]; /* 733 */ char date [ISODCL (19, 25)]; /* 7 by 711 */ char flags [ISODCL (26, 26)]; char file_unit_size [ISODCL (27, 27)]; /* 711 */ char interleave [ISODCL (28, 28)]; /* 711 */ char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ unsigned char name_len [ISODCL (33, 33)]; /* 711 */ char name [0]; } __attribute__((packed)); #define ISOFS_BLOCK_BITS 11 #define ISOFS_BLOCK_SIZE 2048 #define ISOFS_BUFFER_SIZE(INODE) ((INODE)->i_sb->s_blocksize) #define ISOFS_BUFFER_BITS(INODE) ((INODE)->i_sb->s_blocksize_bits) #define ISOFS_SUPER_MAGIC 0x9660 #ifdef __KERNEL__ /* Number conversion inlines, named after the section in ISO 9660 they correspond to. */ #include #include #include #include static inline struct isofs_sb_info *ISOFS_SB(struct super_block *sb) { return sb->s_fs_info; } static inline struct iso_inode_info *ISOFS_I(struct inode *inode) { return container_of(inode, struct iso_inode_info, vfs_inode); } static inline int isonum_711(char *p) { return *(u8 *)p; } static inline int isonum_712(char *p) { return *(s8 *)p; } static inline unsigned int isonum_721(char *p) { return le16_to_cpu(get_unaligned((__le16 *)p)); } static inline unsigned int isonum_722(char *p) { return be16_to_cpu(get_unaligned((__le16 *)p)); } static inline unsigned int isonum_723(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le16_to_cpu(get_unaligned((__le16 *)p)); } static inline unsigned int isonum_731(char *p) { return le32_to_cpu(get_unaligned((__le32 *)p)); } static inline unsigned int isonum_732(char *p) { return be32_to_cpu(get_unaligned((__le32 *)p)); } static inline unsigned int isonum_733(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le32_to_cpu(get_unaligned((__le32 *)p)); } extern int iso_date(char *, int); struct inode; /* To make gcc happy */ extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *); extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *); int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); extern struct dentry *isofs_lookup(struct inode *, struct dentry *, struct nameidata *); extern struct buffer_head *isofs_bread(struct inode *, sector_t); extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long); extern struct inode *isofs_iget(struct super_block *sb, unsigned long block, unsigned long offset); /* Because the inode number is no longer relevant to finding the * underlying meta-data for an inode, we are free to choose a more * convenient 32-bit number as the inode number. The inode numbering * scheme was recommended by Sergey Vlasov and Eric Lammerts. */ static inline unsigned long isofs_get_ino(unsigned long block, unsigned long offset, unsigned long bufbits) { return (block << (bufbits - 5)) | (offset >> 5); } /* Every directory can have many redundant directory entries scattered * throughout the directory tree. First there is the directory entry * with the name of the directory stored in the parent directory. * Then, there is the "." directory entry stored in the directory * itself. Finally, there are possibly many ".." directory entries * stored in all the subdirectories. * * In order for the NFS get_parent() method to work and for the * general consistency of the dcache, we need to make sure the * "i_iget5_block" and "i_iget5_offset" all point to exactly one of * the many redundant entries for each directory. We normalize the * block and offset by always making them point to the "." directory. * * Notice that we do not use the entry for the directory with the name * that is located in the parent directory. Even though choosing this * first directory is more natural, it is much easier to find the "." * entry in the NFS get_parent() method because it is implicitly * encoded in the "extent + ext_attr_length" fields of _all_ the * redundant entries for the directory. Thus, it can always be * reached regardless of which directory entry you have in hand. * * This works because the "." entry is simply the first directory * record when you start reading the file that holds all the directory * records, and this file starts at "extent + ext_attr_length" blocks. * Because the "." entry is always the first entry listed in the * directories file, the normalized "offset" value is always 0. * * You should pass the directory entry in "de". On return, "block" * and "offset" will hold normalized values. Only directories are * affected making it safe to call even for non-directory file * types. */ static inline void isofs_normalize_block_and_offset(struct iso_directory_record* de, unsigned long *block, unsigned long *offset) { /* Only directories are normalized. */ if (de->flags[0] & 2) { *offset = 0; *block = (unsigned long)isonum_733(de->extent) + (unsigned long)isonum_711(de->ext_attr_length); } } extern struct inode_operations isofs_dir_inode_operations; extern struct file_operations isofs_dir_operations; extern struct address_space_operations isofs_symlink_aops; extern struct export_operations isofs_export_ops; /* The following macros are used to check for memory leaks. */ #ifdef LEAK_CHECK #define free_s leak_check_free_s #define malloc leak_check_malloc #define sb_bread leak_check_bread #define brelse leak_check_brelse extern void * leak_check_malloc(unsigned int size); extern void leak_check_free_s(void * obj, int size); extern struct buffer_head * leak_check_bread(struct super_block *sb, int block); extern void leak_check_brelse(struct buffer_head * bh); #endif /* LEAK_CHECK */ #endif /* __KERNEL__ */ #endif fuseiso-v20260614/linux/rock.h000066400000000000000000000052211521353471600161000ustar00rootroot00000000000000// this file was borrowed from linux kernel // originally it has name /usr/src/linux/fs/isofs/rock.h // unfortunately there was no copyright header on it, // so i`m assume it have copyrighted under terms of // GNU GENERAL PUBLIC LICENSE version 2 // as a whole linux kernel // copy of this license included into fuseiso // distribution in file COPYING /* These structs are used by the system-use-sharing protocol, in which the Rock Ridge extensions are embedded. It is quite possible that other extensions are present on the disk, and this is fine as long as they all use SUSP */ struct SU_SP{ unsigned char magic[2]; unsigned char skip; } __attribute__((packed)); struct SU_CE{ char extent[8]; char offset[8]; char size[8]; }; struct SU_ER{ unsigned char len_id; unsigned char len_des; unsigned char len_src; unsigned char ext_ver; char data[0]; } __attribute__((packed)); struct RR_RR{ char flags[1]; } __attribute__((packed)); struct RR_PX{ char mode[8]; char n_links[8]; char uid[8]; char gid[8]; }; struct RR_PN{ char dev_high[8]; char dev_low[8]; }; struct SL_component{ unsigned char flags; unsigned char len; char text[0]; } __attribute__((packed)); struct RR_SL{ unsigned char flags; struct SL_component link; } __attribute__((packed)); struct RR_NM{ unsigned char flags; char name[0]; } __attribute__((packed)); struct RR_CL{ char location[8]; }; struct RR_PL{ char location[8]; }; struct stamp{ char time[7]; } __attribute__((packed)); struct RR_TF{ char flags; struct stamp times[0]; /* Variable number of these beasts */ } __attribute__((packed)); /* Linux-specific extension for transparent decompression */ struct RR_ZF{ char algorithm[2]; char parms[2]; char real_size[8]; }; /* These are the bits and their meanings for flags in the TF structure. */ #define TF_CREATE 1 #define TF_MODIFY 2 #define TF_ACCESS 4 #define TF_ATTRIBUTES 8 #define TF_BACKUP 16 #define TF_EXPIRATION 32 #define TF_EFFECTIVE 64 #define TF_LONG_FORM 128 struct rock_ridge{ char signature[2]; unsigned char len; unsigned char version; union{ struct SU_SP SP; struct SU_CE CE; struct SU_ER ER; struct RR_RR RR; struct RR_PX PX; struct RR_PN PN; struct RR_SL SL; struct RR_NM NM; struct RR_CL CL; struct RR_PL PL; struct RR_TF TF; struct RR_ZF ZF; } u; }; #define RR_PX 1 /* POSIX attributes */ #define RR_PN 2 /* POSIX devices */ #define RR_SL 4 /* Symbolic link */ #define RR_NM 8 /* Alternate Name */ #define RR_CL 16 /* Child link */ #define RR_PL 32 /* Parent link */ #define RR_RE 64 /* Relocation directory */ #define RR_TF 128 /* Timestamps */ fuseiso-v20260614/meson.build000066400000000000000000000010131521353471600157670ustar00rootroot00000000000000project('fuseiso', 'c', version: '20260507', license: 'GPL-2.0-only', default_options: ['warning_level=2'], ) config_h = configuration_data() config_h.set_quoted('VERSION', meson.project_version()) configure_file(output: 'config.h', configuration: config_h) fuse_dep = dependency('fuse3', version: '>=3.14') glib_dep = dependency('glib-2.0', version: '>=2.2') zlib_dep = dependency('zlib') subdir('src') install_data('AUTHORS', 'COPYING', 'NEWS', 'README', install_dir: get_option('datadir') / 'doc' / 'fuseiso') fuseiso-v20260614/src/000077500000000000000000000000001521353471600144215ustar00rootroot00000000000000fuseiso-v20260614/src/fuseiso.c000066400000000000000000000301431521353471600162430ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2005, 2006 by Dmitry Morozhnikov * * dmiceman@mail.ru * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FUSE_USE_VERSION 314 #include #include #include #include #include "isofs.h" #ifdef __GNUC__ # define UNUSED(x) x __attribute__((unused)) #else # define UNUSED(x) x #endif static char *imagefile = NULL; static char *mount_point = NULL; static int image_fd = -1; int maintain_mount_point; int maintain_mtab; char* iocharset; char* normalize_name(const char* fname) { char* abs_fname = (char *) malloc(PATH_MAX); realpath(fname, abs_fname); // ignore errors from realpath() return abs_fname; }; int check_mount_point() { struct stat st; int rc = lstat(mount_point, &st); if(rc == -1 && errno == ENOENT) { // directory does not exists, createcontext rc = mkdir(mount_point, 0777); // let`s underlying filesystem manage permissions if(rc != 0) { perror("Can`t create mount point"); return -EIO; }; } else if(rc == -1) { perror("Can`t check mount point"); return -1; }; return 0; }; void del_mount_point() { int rc = rmdir(mount_point); if(rc != 0) { perror("Can`t delete mount point"); }; }; char* get_mtab_path() { char* mtab_path = (char*) malloc(PATH_MAX); uid_t uid = getuid(); struct passwd* passwd = getpwuid(uid); if(!passwd) { fprintf(stderr, "Can`t get home directory for user %d: %s\n", uid, strerror(errno)); return NULL; }; mtab_path[0] = 0; if(passwd->pw_dir) { // may be NULL, who know.. strncpy(mtab_path, passwd->pw_dir, PATH_MAX - 16); mtab_path[PATH_MAX - 1] = 0; }; strcat(mtab_path, "/.mtab.fuseiso"); return mtab_path; }; int add_mtab_record() { int rc; char* mtab_path = get_mtab_path(); if(!mtab_path) { return -EIO; }; int fd = open(mtab_path, O_RDWR | O_CREAT, 0644); if(fd < 0) { perror("Can`t open mtab"); return -EIO; }; rc = lockf(fd, F_LOCK, 0); if(rc != 0) { perror("Can`t lock mtab"); return -EIO; }; FILE* mtab = setmntent(mtab_path, "a"); if(!mtab) { perror("Can`t open mtab"); return -EIO; }; struct mntent ent; ent.mnt_fsname = imagefile; ent.mnt_dir = mount_point; ent.mnt_type = "fuseiso"; ent.mnt_opts = "defaults"; ent.mnt_freq = 0; ent.mnt_passno = 0; rc = addmntent(mtab, &ent); if(rc != 0) { perror("Can`t add mtab entry"); return -EIO; }; endmntent(mtab); rc = lockf(fd, F_ULOCK, 0); if(rc != 0) { perror("Can`t unlock mtab"); return -EIO; }; close(fd); free(mtab_path); return 0; }; int del_mtab_record() { int rc; char* mtab_path = get_mtab_path(); char new_mtab_path[PATH_MAX]; if(!mtab_path) { return -EIO; }; int fd = open(mtab_path, O_RDWR | O_CREAT, 0644); if(fd < 0) { perror("Can`t open mtab"); return -EIO; }; rc = lockf(fd, F_LOCK, 0); if(rc != 0) { perror("Can`t lock mtab"); return -EIO; }; strncpy(new_mtab_path, mtab_path, PATH_MAX - 16); new_mtab_path[PATH_MAX - 1] = 0; strcat(new_mtab_path, ".new"); FILE* mtab = setmntent(mtab_path, "r"); if(!mtab) { perror("Can`t open mtab"); return -EIO; }; FILE* new_mtab = setmntent(new_mtab_path, "a+"); if(!new_mtab) { perror("Can`t open new mtab"); return -EIO; }; struct mntent* ent; while((ent = getmntent(mtab)) != NULL) { if(strcmp(ent->mnt_fsname, imagefile) == 0 && strcmp(ent->mnt_dir, mount_point) == 0 && strcmp(ent->mnt_type, "fuseiso") == 0) { // skip } else { rc = addmntent(new_mtab, ent); if(rc != 0) { perror("Can`t add mtab entry"); return -EIO; }; }; }; endmntent(mtab); endmntent(new_mtab); rc = rename(new_mtab_path, mtab_path); if(rc != 0) { perror("Can`t rewrite mtab"); return -EIO; }; rc = lockf(fd, F_ULOCK, 0); if(rc != 0) { perror("Can`t unlock mtab"); return -EIO; }; close(fd); free(mtab_path); return 0; }; static int isofs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *UNUSED(fi)) { return isofs_real_getattr(path, stbuf); } static int isofs_readlink(const char *path, char *target, size_t size) { return isofs_real_readlink(path, target, size); }; static int isofs_open(const char *path, struct fuse_file_info *UNUSED(fi)) { return isofs_real_open(path); } static int isofs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *UNUSED(fi)) { return isofs_real_read(path, buf, size, offset); } static int isofs_flush(const char *UNUSED(path), struct fuse_file_info *UNUSED(fi)) { return 0; }; static void* isofs_init(struct fuse_conn_info *UNUSED(conn), struct fuse_config *cfg) { int rc; if(maintain_mtab) { rc = add_mtab_record(); if(rc != 0) { exit(EXIT_FAILURE); }; }; cfg->use_ino = 1; return isofs_real_init(); }; static void isofs_destroy(void* param) { (void)param; if(maintain_mtab) { del_mtab_record(); }; return; }; static int isofs_opendir(const char *path, struct fuse_file_info *UNUSED(fi)) { return isofs_real_opendir(path); }; static int isofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t UNUSED(offset), struct fuse_file_info *UNUSED(fi), enum fuse_readdir_flags UNUSED(flags)) { return isofs_real_readdir(path, buf, filler); }; static int isofs_statfs(const char *UNUSED(path), struct statvfs *stbuf) { return isofs_real_statfs(stbuf); } static struct fuse_operations isofs_oper = { .getattr = isofs_getattr, .readlink = isofs_readlink, .open = isofs_open, .read = isofs_read, .flush = isofs_flush, .init = isofs_init, .destroy = isofs_destroy, .opendir = isofs_opendir, .readdir = isofs_readdir, .statfs = isofs_statfs, }; void usage(const char* prog) { printf("Version: %s\nUsage: %s [-n] [-p] [-c ] [-h] []\n" "Where options are:\n" " -n -- do NOT maintain file $HOME/.mtab.fuseiso\n" " -p -- maintain mount point; \n" " create it if it does not exists and delete it on exit\n" " -c -- specify iocharset for Joliet filesystem\n" " -h -- print this screen\n" "\nCommon FUSE library options are:\n" " -o -- specify comma-separated list of mount options\n" " -s -- run single-threaded\n" " -f -- run in foreground, do not daemonize\n" " -d -- run in foreground and print debug information\n" " -V -- print libfuse version information\n" "\nPlease consult with FUSE documentation for more information\n", VERSION, prog); }; int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); // set current locale for proper iocharset // defaults maintain_mount_point = 0; maintain_mtab = 1; iocharset = NULL; char **fuseopts = reallocarray(NULL, 1, sizeof(*fuseopts)); fuseopts[0] = argv[0]; size_t fuseopts_cnt = 1; int c; char *s; while((c = getopt(argc, argv, "+npc:ho:sfdV")) > 0) { switch((char)c) { case 'n': maintain_mtab = 0; break; case 'p': maintain_mount_point = 1; break; case 'c': if(optarg) { iocharset = optarg; }; break; case 'h': usage(argv[0]); exit(0); break; case 'o': fuseopts = reallocarray(fuseopts, fuseopts_cnt + 2, sizeof(*fuseopts)); fuseopts[fuseopts_cnt++] = "-o"; fuseopts[fuseopts_cnt++] = optarg; break; case 's': case 'f': case 'd': fuseopts = reallocarray(fuseopts, fuseopts_cnt + 1, sizeof(*fuseopts)); s = (char *)malloc(3); snprintf(s, 3, "-%c", c); fuseopts[fuseopts_cnt++] = s; break; case 'V': fuseopts = reallocarray(fuseopts, fuseopts_cnt + 1, sizeof(*fuseopts)); fuseopts[fuseopts_cnt++] = "-V"; return fuse_main(fuseopts_cnt, fuseopts, &isofs_oper, NULL); break; case '?': case ':': usage(argv[0]); exit(EXIT_FAILURE); break; }; }; if((argc - optind) < 2) { usage(argv[0]); exit(EXIT_FAILURE); }; imagefile = normalize_name(argv[optind++]); image_fd = open(imagefile, O_RDONLY); if(image_fd == -1) { fprintf(stderr, "Supplied image file name: \"%s\"\n", imagefile); perror("Can`t open image file"); exit(EXIT_FAILURE); }; mount_point = normalize_name(argv[optind]); fuseopts = reallocarray(fuseopts, fuseopts_cnt + argc - optind, sizeof(*fuseopts)); while(optind < argc) { fuseopts[fuseopts_cnt++] = argv[optind++]; }; if(!iocharset) { char *nlcharset = nl_langinfo(CODESET); if(nlcharset) { #ifdef __GLIBC__ iocharset = (char *) malloc(strlen(nlcharset) + 9); strcpy(iocharset, nlcharset); strcat(iocharset, "//IGNORE"); #else iocharset = (char *) malloc(strlen(nlcharset) + 1); strcpy(iocharset, nlcharset); #endif }; if(!iocharset) { #ifdef __GLIBC__ iocharset = "UTF-8//IGNORE"; #else iocharset = "UTF-8"; #endif }; }; int rc; if(maintain_mount_point) { rc = check_mount_point(); if(rc != 0) { exit(EXIT_FAILURE); }; }; if(maintain_mount_point) { rc = atexit(del_mount_point); if(rc != 0) { fprintf(stderr, "Can`t set exit function\n"); exit(EXIT_FAILURE); } }; // will exit in case of failure rc = isofs_real_preinit(imagefile, image_fd); return fuse_main(fuseopts_cnt, fuseopts, &isofs_oper, NULL); }; fuseiso-v20260614/src/isofs.c000066400000000000000000002213341521353471600157150ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2005, 2006 by Dmitry Morozhnikov * * dmiceman@mail.ru * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ // for struct tm->tm_gmtoff #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "isofs.h" #define ARR_LENGTH(arr) \ (sizeof(arr) == 0) ? (0) : (sizeof(arr) / sizeof(arr[0])) static isofs_context context; static GHashTable *lookup_table; static GHashTable *negative_lookup_table; static char *negative_value = "not exists"; static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER; static int isofs_check_rr(struct iso_directory_record *root_record); static ssize_t isofs_read_raw_block(int block, char *buf); static void identify_iso(); static int read_next_volume_descriptor(); struct iso_volume_descriptor vd; typedef struct _iso_definition { size_t block_size; size_t block_offset; int file_offset; } iso_definition; extern char* iocharset; // locally implement g_strv_length, this is missing in glib2 for rhel3/rhel4 // -- Chandan Dutta Chowdhury 2007-07-06 guint local_g_strv_length (gchar **str_array) { guint i = 0; g_return_val_if_fail (str_array != NULL, 0); while (str_array[i]) ++i; return i; }; int isofs_real_preinit( char* imagefile, int fd) { memset(&context, 0, sizeof(isofs_context)); context.imagefile = imagefile; context.fd = fd; identify_iso(); /* printf("CD001 found at %d, bs %d, boff %d, ds %d\n", context.id_offset, context.block_size, context.block_offset, context.data_size);*/ while(read_next_volume_descriptor() != ISO_VD_END) { int vd_type = isonum_711((unsigned char *)(vd.type)); // printf("init: found volume descriptor type %d, vd_num %d\n", vd_type, vd_num); switch(vd_type) { case ISO_VD_PRIMARY: // check if this is only primary descriptor found if(context.pd.type[0]) { fprintf(stderr, "init: primary volume descriptor already found, skipping..\n"); } else { memcpy(&context.pd, &vd, sizeof(struct iso_volume_descriptor)); context.root = (struct iso_directory_record *)& context.pd.root_directory_record; context.data_size = isonum_723(context.pd.logical_block_size); if(!context.data_size) { fprintf(stderr, "init: wrong block data size %zu, using default 2048\n", context.data_size); context.data_size = 2048; }; if(isofs_check_rr(context.root)) { context.pd_have_rr = 1; }; }; break; case ISO_VD_SUPPLEMENTARY: { struct iso_supplementary_descriptor *sd = (struct iso_supplementary_descriptor *)&vd; if(!context.pd.type[0]) { fprintf(stderr, "init: supplementary volume descriptor found, but no primary descriptor!\n"); exit(EIO); } else { int joliet_level = 0; if(sd->escape[0] == 0x25 && sd->escape[1] == 0x2f) { switch(sd->escape[2]) { case 0x40: joliet_level = 1; break; case 0x43: joliet_level = 2; break; case 0x45: joliet_level = 3; break; }; }; int have_rr = isofs_check_rr((struct iso_directory_record *) sd->root_directory_record); // switch to SVD only if it contain RRIP or if PVD have no RRIP // in other words, prefer VD with RRIP if((joliet_level && have_rr) || (have_rr && !context.pd_have_rr) || (joliet_level && !context.pd_have_rr)) { context.joliet_level = joliet_level; memcpy(&context.sd, &vd, sizeof(struct iso_volume_descriptor)); context.supplementary = 1; context.root = (struct iso_directory_record *) context.sd.root_directory_record; // printf("init: switching to supplementary descriptor %d, joliet_level %d, have_rr %d\n", // vd_num, context.joliet_level, have_rr); } else { context.joliet_level = 0; // printf("init: found supplementary descriptor %d, flags %d\n", // vd_num, isonum_711(sd->flags)); }; }; }; break; case 0: // boot record, not intresting.. break; default: fprintf(stderr, "init: unsupported volume descriptor type %d\n", vd_type); break; } }; if(!context.pd.type[0]) { fprintf(stderr, "init: primary volume descriptor not found! exiting..\n"); exit(EIO); }; context.susp = 0; context.susp_skip = 0; lookup_table = g_hash_table_new(g_str_hash, g_str_equal); negative_lookup_table = g_hash_table_new(g_str_hash, g_str_equal); isofs_inode *inode = (isofs_inode *) malloc(sizeof(isofs_inode)); if(!inode) { perror("Can`t malloc: "); exit(ENOMEM); }; memset(inode, 0, sizeof(isofs_inode)); inode->record = context.root; context.last_ino++; // set to 1 inode->st_ino = context.last_ino; context.last_ino++; g_hash_table_insert(lookup_table, "/", inode); return 0; }; static void identify_iso() { struct iso_volume_descriptor vd; // defaults for iso context.block_size = 2048; context.data_size = 2048; context.block_offset = 0; context.file_offset = 0; iso_definition iso_defs[] = { {2048, 0, 307200}, // NRG {2048, 0, 0}, // ISO_2048 {2352, 0, 0}, // MODE1_2352_RAW {2352, 16, 0}, // MODE1_2352 {2352, 24, 0}, // MODE2_2352 {2336, 16, 0}, // MODE2_2336 }; // try to find CD001 identifier int i; for(i = 0; i < ARR_LENGTH(iso_defs); i++) { int id_offset = iso_defs[i].block_size * 16 + iso_defs[i].block_offset + iso_defs[i].file_offset; if(lseek(context.fd, id_offset, SEEK_SET) == -1) { fprintf(stderr, "can`t lseek() to next possible data start position (%d); is it really supported file?", id_offset); exit(EIO); }; ssize_t size = read(context.fd, &vd, sizeof(struct iso_volume_descriptor)); if(size != sizeof(struct iso_volume_descriptor)) { fprintf(stderr, "only %zd bytes read from position %d, %zu required; is it really a supported file?\n", size, id_offset, sizeof(struct iso_volume_descriptor)); exit(EIO); }; char *vd_id = (char *) (vd.id); if(strncmp("CD001", vd_id, 5) == 0) { // found CD001! // fill context with information about block size and block offsets context.data_size = 2048; context.id_offset = id_offset; context.block_size = iso_defs[i].block_size; context.block_offset = iso_defs[i].block_offset; context.file_offset = iso_defs[i].file_offset; break; }; }; } static int read_next_volume_descriptor() { static int vd_num = 0; if(lseek(context.fd, context.id_offset + context.block_size * vd_num, SEEK_SET) == -1) { perror("can`t lseek() to next volume descriptor"); exit(EIO); } ssize_t size = read(context.fd, &vd, sizeof(struct iso_volume_descriptor)); if(size != sizeof(struct iso_volume_descriptor)) { fprintf(stderr, "only %zd bytes read from volume descriptor %d, %zu required\n", size, vd_num, sizeof(struct iso_volume_descriptor)); exit(EIO); } if(strncmp("CD001", vd.id, 5) != 0) { if(vd_num > 16) { // no more trying fprintf(stderr, "init: wrong standard identifier in volume descriptor %d, exiting..\n", vd_num); exit(EIO); } else { // try to continue fprintf(stderr, "init: wrong standard identifier in volume descriptor %d, skipping..\n", vd_num); } } vd_num++; return isonum_711((unsigned char *)(vd.type)); } static char* dstr(char* str, const char* src, int len) { int i; strncpy(str, src, len); str[len] = '\0'; for(i = len - 1; i >= 0; --i) { if(str[i] == '\0' || str[i] == ' ' || str[i] == '\t' || str[i] == '\r' || str[i] == '\n') { str[i] = '\0'; } else { return str; }; }; return str; }; void* isofs_real_init() { if(context.file_offset == 307200) { printf("NRG image found\n"); } else if(context.block_size == 2048) { printf("ISO9660 image found\n"); } else if(context.block_size == 2352 && context.block_offset == 0) { printf("MODE2 RAW BIN image found\n"); } else if(context.block_size == 2352 && context.block_offset == 16) { printf("MODE1 BIN image found (or CCD MODE1 image, or MDF image)\n"); } else if(context.block_size == 2352 && context.block_offset == 24) { printf("MODE2 BIN image found (or CCD MODE2 image)\n"); } else if(context.block_size == 2336 && context.block_offset == 16) { printf("MODE2/2336 BIN image found\n"); } else { printf("UNKNOWN image found; probably will not work\n"); }; if(context.block_size != 2048) { // report unusual data block size printf("Data block size: %zu\n", context.block_size); }; char buf[129]; printf("System Identifier : %s\n", dstr(buf, context.pd.system_id, 32)); printf("Volume Identifier : %.32s\n", dstr(buf, context.pd.volume_id, 32)); printf("Volume Set Identifier : %.128s\n", dstr(buf, context.pd.volume_set_id, 128)); printf("Publisher Identifier : %.128s\n", dstr(buf, context.pd.publisher_id, 128)); printf("Data Preparer Identifier : %.128s\n", dstr(buf, context.pd.preparer_id, 128)); printf("Application Identifier : %.128s\n", dstr(buf, context.pd.application_id, 128)); printf("Copyright File Identifier : %.37s\n", dstr(buf, context.pd.copyright_file_id, 37)); printf("Abstract File Identifier : %.37s\n", dstr(buf, context.pd.abstract_file_id, 37)); printf("Bibliographic File Identifier : %.37s\n", dstr(buf, context.pd.bibliographic_file_id, 37)); printf("Volume Creation Date and Time : %.17s\n", dstr(buf, context.pd.creation_date, 17)); printf("Volume Modification Date and Time : %.17s\n", dstr(buf, context.pd.modification_date, 17)); printf("Volume Expiration Date and Time : %.17s\n", dstr(buf, context.pd.expiration_date, 17)); printf("Volume Effective Date and Time : %.17s\n", dstr(buf, context.pd.effective_date, 17)); return (void*) &context; }; static int isofs_check_rr(struct iso_directory_record *root_record) { int extent = isonum_733(root_record->extent); char *buf = (char *) malloc(context.data_size); // can we use "standard" 2048 there? if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; ssize_t rc = isofs_read_raw_block(extent, buf); if(rc < 0) { free(buf); return 0; }; struct iso_directory_record *record = (struct iso_directory_record *) buf; size_t record_length = isonum_711((unsigned char*) record->length); size_t name_len = isonum_711(record->name_len); size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len; if(record_length < sizeof(struct iso_directory_record)) { fprintf(stderr, "check_rr: directory record length too small: %zu\n", record_length); free(buf); return -EIO; }; if(name_len != 1) { fprintf(stderr, "check_rr: file name length too big for . record: %zu\n", name_len); free(buf); return -EIO; }; if(record_length < name_len + sizeof(struct iso_directory_record) + pad_len) { // probably something wrong with name_len fprintf(stderr, "check_rr: wrong name_len in directory entry: %zu, record_length %zu\n", name_len, record_length); free(buf); return -EIO; }; if(sa_len >= 7) { struct rock_ridge *sue = (struct rock_ridge *) (((char *) record) + sizeof(struct iso_directory_record) + name_len + pad_len); int sue_sig = SIG(sue->signature[0], sue->signature[1]); int sue_len = sue->len; int sue_version = sue->version; if(sue_sig == SIG('S', 'P')) { if(sue_len != 7 || sue_version != 1 || sue->u.SP.magic[0] != 0xbe || sue->u.SP.magic[1] != 0xef) { // incorrect SP entry free(buf); return 0; } else { // got it! free(buf); return 1; }; } else { // not SP record free(buf); return 0; }; } else { // no space for SP record free(buf); return 0; }; // should not happen free(buf); return 0; }; static isofs_inode *isofs_lookup(const char *path) { if(path[0] == '\0') { return NULL; }; isofs_inode *inode = g_hash_table_lookup(lookup_table, path); if(inode) { return inode; }; if(g_hash_table_lookup(negative_lookup_table, path)) { return NULL; }; // printf("start search for %s\n", path); gchar **parts = g_strsplit(path, "/", -1); guint parts_len = local_g_strv_length(parts); unsigned int partno = 1; gchar *rpath = g_strdup("/"); gchar *rpath1 = ""; gchar *part = parts[partno]; while(part && partno < parts_len) { rpath1 = g_strconcat(rpath1, "/", part, NULL); // printf("looking for %s in %s...\n", rpath1, rpath); inode = g_hash_table_lookup(lookup_table, rpath1); if(!inode) { // printf("trying to load %s...\n", rpath); int rc = isofs_real_readdir(rpath, NULL, NULL); if(rc) { fprintf(stderr, "lookup: error %d from readdir: %s\n", rc, strerror(-rc)); g_strfreev(parts); g_free(rpath); return NULL; }; }; partno++; part = parts[partno]; g_free(rpath); rpath = rpath1; }; g_strfreev(parts); g_free(rpath); inode = g_hash_table_lookup(lookup_table, path); if(!inode) { g_hash_table_insert(negative_lookup_table, g_strdup(path), negative_value); }; return inode; }; static ssize_t isofs_read_raw_block(int block, char *buf) { off_t off = block * context.block_size + context.block_offset + context.file_offset; if(pthread_mutex_lock(& fd_mutex)) { int err = errno; perror("isofs_read_raw_block: can`l lock fd_mutex"); return -err; }; if(lseek(context.fd, off, SEEK_SET) == -1) { perror("isofs_read_raw_block: can`t lseek()"); pthread_mutex_unlock(& fd_mutex); return -EIO; }; size_t len = read(context.fd, buf, context.data_size); if(len != context.data_size) { fprintf(stderr, "isofs_read_raw_block: can`t read full block, read only %zu bytes from offset %d, %zu required; errno %d, message %s\n", len, (int) off, context.data_size, errno, strerror(errno)); fprintf(stderr, "isofs_read_raw_block: huh? reading zeros beyond file end? someone want to save a penny?\n"); memset(buf + len, 0, context.data_size - len); // pthread_mutex_unlock(& fd_mutex); // return -EIO; }; pthread_mutex_unlock(& fd_mutex); // printf("block %d, offset %d, read %d\n", block, (int) off, len); return len; }; static time_t isofs_date(char *stamp, int stamp_len) { struct tm tm; memset(& tm, 0, sizeof(struct tm)); if(stamp_len == 7) { // ISO9660:9.1.5 tm.tm_year = stamp[0]; tm.tm_mon = stamp[1] - 1; tm.tm_mday = stamp[2]; tm.tm_hour = stamp[3]; tm.tm_min = stamp[4]; tm.tm_sec = stamp[5]; tm.tm_isdst = -1; tm.tm_gmtoff = stamp[6] * 15 * 60; } else if(stamp_len == 17) { // ISO9660:8.4.26.1 fprintf(stderr, "isofs_date: 17 byte date isn`t supported for the moment, sorry\n"); return 0; } else { fprintf(stderr, "isofs_date: unsupported date format, stamp_len %d\n", stamp_len); return 0; }; if(tm.tm_gmtoff) { // fprintf(stderr, "direntry2stat: non-zero timezone offset: %d\n", tm.tm_gmtoff); }; time_t time = mktime(& tm); return time; }; static int isofs_direntry2stat(struct stat *st, isofs_inode *inode) { struct iso_directory_record *record = inode->record; // fill st_ino from block number where file start // since there is no files begin in the same block // st->st_ino = isonum_733(record->extent); // but some iso images save space by sharing content between several files // so it is better to save it unique st->st_ino = inode->st_ino; if(inode->SL) { st->st_size = inode->sl_len; } else if(inode->ZF) { // compressed file, report real (uncompressed) size st->st_size = inode->real_size; } else { // no zisofs compression st->st_size = isonum_733(record->size); }; st->st_blocks = (st->st_size + 511) >> 9; // divide by 512 with ceil st->st_blksize = context.data_size; st->st_nlink = 1; // always, even if rrip PX entry found if(inode->PX) { // rrip PX entry found and in effect st->st_mode = inode->st.st_mode; st->st_uid = inode->st.st_uid; st->st_gid = inode->st.st_gid; } else { /// TODO use hidden flag? if(ISO_FLAGS_DIR(record->flags)) { st->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH; // dir, readabale and browsable by everyone } else { st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; // regular, read by everyone }; }; if(inode->TF) { // rrip TF entry found and in effect st->st_atime = inode->st.st_atime; st->st_ctime = inode->st.st_ctime; st->st_mtime = inode->st.st_mtime; } else { if(!inode->ctime) { inode->ctime = isofs_date(record->date, 7); }; st->st_atime = inode->ctime; st->st_ctime = inode->ctime; st->st_mtime = inode->ctime; }; return 0; }; static char *isofs_fix_entry(char *entry, size_t len) { if(!context.joliet_level) { // iso9660 names char *sep2 = index(entry, ';'); // find SEPARATOR2 if(sep2) { // remove remaining part *sep2 = '\0'; }; char *sep1 = rindex(entry, '.'); // find SEPARATOR1 if(sep1 && sep1[1] == '\0') { // check if SEPARATOR1 is a last symbol in string *sep1 = '\0'; // remove it }; // this part is borrowed from linux kernel code // convert remaining ';' and '/' characters to dots // and lowercase characters in range A-Z char *p = entry; while(*p) { if(*p == ';' || *p == '/') { *p = '.'; } else if(*p >= 'A' && *p <= 'Z') { *p |= 0x20; }; p++; }; return entry; } else { // initialize iconv descriptor iconv_t cd = iconv_open(iocharset, "UCS-2BE"); if(cd == (iconv_t)-1) { perror("iconv"); return NULL; }; char *inbuf = entry; size_t inbytesleft = len; char *outbuf = (char *) malloc(NAME_MAX); // this should be sufficient for our purposes if(!outbuf) { perror("Can`t malloc: "); return NULL; }; char *outentry = outbuf; size_t outbytesleft = NAME_MAX; int rc = iconv(cd, & inbuf, & inbytesleft, & outbuf, & outbytesleft); size_t outlen = NAME_MAX - outbytesleft; outentry[outlen] = '\0'; if(rc == -1) { // incorrect multibyte char or other iconv error -- return as much as possible anyway fprintf(stderr, "iconv on '%s': %s\n", outentry, strerror(errno)); if(outlen == 0) { iconv_close(cd); free(entry); free(outentry); return NULL; }; // try to recover }; // printf("outlen %d, outbytesleft %d, rc %d, outbuf %s\n", outlen, outbytesleft, rc, outentry); // borrowed from linux kernel isofs joliet code if(outlen > 2 && outentry[outlen - 2] == ';' && outentry[outlen - 1] == '1') { outentry[outlen - 2] = '\0'; }; if(outlen >= 2 && outentry[outlen - 1] == '.') { outentry[outlen - 1] = '\0'; }; free(entry); iconv_close(cd); return outentry; }; }; static void isofs_free_inode(isofs_inode *inode) { if(inode->SL && inode->sl) { free(inode->sl); }; if(inode->NM && inode->nm) { free(inode->nm); }; if(inode->zf_blockptr) { free(inode->zf_blockptr); }; if(inode->record) { free(inode->record); }; free(inode); }; // borrowed from zisofs-tools static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; static int isofs_parse_zisofs_header(isofs_inode *inode) { char *buf = (char *) malloc(context.block_size); if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; int block_num = isonum_733(inode->record->extent); ssize_t len = isofs_read_raw_block(block_num, buf); if(len < 0) { free(buf); return len; }; if((size_t)len < inode->zf_header_size) { fprintf(stderr, "isofs_parse_zisofs_header: too small block len %zd\n", len); free(buf); return -EIO; }; zf_file_header *zf_header = (zf_file_header *) buf; if(memcmp(zf_header->magic, zisofs_magic, sizeof(zisofs_magic))) { // invalid compressed file header free(buf); return 1; }; if(inode->zf_block_shift < 0 || inode->zf_block_shift > 30) { fprintf(stderr, "isofs_parse_zisofs_header: invalid block shift %d\n", inode->zf_block_shift); free(buf); return -EIO; } size_t block_size = 1UL << inode->zf_block_shift; inode->zf_nblocks = ((inode->real_size + inode->zf_header_size - 1) / block_size) + 1; size_t block_table_size = (inode->zf_nblocks + 1) * 4; if(!inode->zf_blockptr) { inode->zf_blockptr = (int *) malloc(block_table_size); if(!inode->zf_blockptr) { perror("Can`t malloc: "); return -ENOMEM; }; }; // copy offset table into memory buffer, maintaining iso9660 block boundaries size_t block_table_left = block_table_size; size_t block_table_total = 0; size_t block_table_shift = inode->zf_header_size; while(block_table_left) { size_t block_table_chunk = (block_table_left < context.data_size - block_table_shift ? block_table_left : context.data_size - block_table_shift); /* printf("isofs_parse_sa: block_table_size: %d, block_table_left: %d, block_table_total %d, block_table_shift %d, block_table_chunk %d\n", block_table_size, block_table_left, block_table_total, block_table_shift, block_table_chunk);*/ memcpy(((char *) inode->zf_blockptr) + block_table_total, buf + block_table_shift, block_table_chunk); block_table_left -= block_table_chunk; block_table_total += block_table_chunk; block_table_shift = 0; // read next block block_num += 1; len = isofs_read_raw_block(block_num, buf); if(len < 0) { free(buf); return len; }; /* printf("isofs_parse_sa: block_num: %d, len: %d\n", block_num, len);*/ }; /* printf("isofs_parse_zisofs_header: real size %d, header size %d, nblocks %d, block size %d\n", inode->real_size, inode->zf_header_size, inode->zf_nblocks, block_size); int i; for(i = 0; i <= inode->zf_nblocks; i++) { printf("zf block table entry %d have value %d\n", i, inode->zf_blockptr[i]); };*/ // all information for compressed file we have already in ZF entry // so just signal what this is really compressed file free(buf); return 0; }; static int isofs_parse_sa(isofs_inode *inode, char *sa, size_t sa_len) { int cont_block = 0; unsigned int cont_offset = 0; unsigned int cont_size = 0; int remaining = sa_len; while(remaining > 4) { // susp 4. // printf("parse_sa: sa offset %d, remaining %d\n", sa_len - remaining, remaining); struct rock_ridge *sue = (struct rock_ridge *) (sa + sa_len - remaining); int sue_sig = SIG(sue->signature[0], sue->signature[1]); int sue_len = sue->len; int sue_version = sue->version; /* printf("parse_sa: signature %c%c, sue_len %d, sue_version %d\n", sue->signature[0], sue->signature[1], sue_len, sue_version);*/ int known_sue = 1; switch(sue_sig) { case SIG('S', 'P'): // susp 5.3 if(sue_len != 7 || sue_version != 1 || sue->u.SP.magic[0] != 0xbe || sue->u.SP.magic[1] != 0xef) { // incorrect SP entry fprintf(stderr, "parse_sa: incorrect SP entry: sue_len %d, sue_version %d, magic %c%c\n", sue_len, sue_version, sue->u.SP.magic[0], sue->u.SP.magic[1]); context.susp = 0; return 0; } else { context.susp = 1; context.susp_skip = sue->u.SP.skip; }; // printf("parse_sa: SP entry, skip %d\n", sue->u.SP.skip); break; case SIG('C', 'E'): // susp 5.1 if(sue_len != 28 || sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect CE entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else if(cont_block != 0) { // CE entry already found fprintf(stderr, "parse_sa: duplicate CE entry, skipping, sue_len %d, sue_version %d\n", sue_len, sue_version); } else { cont_block = isonum_733(sue->u.CE.extent); cont_offset = isonum_733(sue->u.CE.offset); cont_size = isonum_733(sue->u.CE.size); if(cont_block < 16) { // continuation area can`t be there fprintf(stderr, "parse_sa: wrong continuation area extent: %d, cont_offset %u, cont_size %u\n", cont_block, cont_offset, cont_size); cont_block = 0; // do not process it } else if(cont_offset + cont_size > context.data_size) { // something wrong with continuation area offset and/or size fprintf(stderr, "parse_sa: wrong continuation area: extent %d, cont_offset %u, cont_size %u\n", cont_block, cont_offset, cont_size); cont_block = 0; // do not process it } else { /* printf("parse_sa: CE entry, extent %d, offset %d, size %d\n", cont_block, cont_offset, cont_size);*/ }; }; break; case SIG('E', 'R'): // susp 5.5 if(sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect ER entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int len_id = sue->u.ER.len_id; int len_des = sue->u.ER.len_des; int len_src = sue->u.ER.len_src; int ext_ver = sue->u.ER.ext_ver; if(len_id + len_des + len_src + 8 > sue_len) { fprintf(stderr, "parse_sa: incorrect ER entry: sue_len %d, sue_version %d, len_id %d, len_des %d, len_src %d, ext_ver %d\n", sue_len, sue_version, len_id, len_des, len_src, ext_ver); } else { char id[256]; char des[256]; char src[256]; strncpy(id, sue->u.ER.data, len_id); id[len_id] = '\0'; strncpy(des, sue->u.ER.data + len_id, len_des); des[len_des] = '\0'; strncpy(src, sue->u.ER.data + len_id + len_des, len_src); src[len_src] = '\0'; /* printf("parse_sa: ER entry:\n\t id: %s\n\tdes: %s\n\tsrc: %s\n\tver: %d\n", id, des, src, ext_ver);*/ }; }; break; case SIG('R', 'R'): { // unused isonum_711((unsigned char *) sue->u.RR.flags); /* printf("parse_sa: RR entry, sue_version %d, sue_len %d, flags %d\n", sue_version, sue_len, rr_flags);*/ }; break; case SIG('P', 'X'): // rrip 4.1.1 // according to rrip 4.1.1, length of PX record should be exactly 44 // but linux kernel and mkisofs seems to be happy with length 36, // where 'serial number' are not presented // (or i`m looking at outdated draft.. :-) if((sue_len != 44 && sue_len != 36) || sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect PX entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { mode_t mode = isonum_733(sue->u.PX.mode); nlink_t nlink = isonum_733(sue->u.PX.n_links); uid_t uid = isonum_733(sue->u.PX.uid); gid_t gid = isonum_733(sue->u.PX.gid); /* printf("parse_sa: PX entry, sue_version %d, sue_len %d, mode %d, nlinks %d, uid %d, gid %d\n", sue_version, sue_len, mode, nlink, uid, gid);*/ inode->st.st_mode = mode; inode->st.st_nlink = nlink; inode->st.st_uid = uid; inode->st.st_gid = gid; inode->PX = 1; /// TODO check if entry length == 44 and set st_ino field from 'file serial number'? }; break; case SIG('S', 'L'): // rrip 4.1.3 if(sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect SL entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else if(inode->SL) { fprintf(stderr, "parse_sa: SL entry already in effect, sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int sl_flags = sue->u.SL.flags; int max_components = (sue_len - 5) / sizeof(struct SL_component); if(max_components < 1) { fprintf(stderr, "parse_sa: SL entry found, but no components: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int c; int c_offset = 0; int c_errors = 0; for(c = 0; c < max_components; c++) { struct SL_component *comp = (struct SL_component *) (((char *) & sue->u.SL.link) + c_offset); int sl_c_flags = comp->flags; int sl_c_len = comp->len; if(c_offset + 5 >= sue_len) { // according to rrip, we must stop if CONTINUE flag isn`t set // according to linux kernel isofs code and images produced witj mkisofs // we need to continue while there is space in SL entry for next component break; // strict rrip code: // fprintf(stderr, // "parse_sa: invalid SL component: sue_len %d, sue_version %d, sl_flags %d, sl_c_flags %d, sl_c_len %d\n", // sue_len, sue_version, sl_flags, sl_c_flags, sl_c_len); // c_errors++; // break; /// TODO find _working_ rrip specification }; int c_len = 0; char c_separator = 0; if(!inode->sl_len) { // nothing found previoustly } else if(inode->sl_len == 1 && inode->sl[0] == '/') { // previous SL component was ROOT // no need for separator after ROOT component } else { c_len += 1; // place for '/' separator c_separator = '/'; }; if(sl_c_flags & (1 << 1)) { // CURRENT component c_len += 1; // place for '.' character } else if(sl_c_flags & (1 << 2)) { // PARENT component c_len += 2; // place for ".." characters } else { c_len += sl_c_len; }; if(c_len + inode->sl_len + 1 > PATH_MAX) { fprintf(stderr, "parse_sa: too long symlink found: sue_len %d, sue_version %d, sl_flags %d, sl_c_flags %d, sl_c_len %d, c_len %d\n", sue_len, sue_version, sl_flags, sl_c_flags, sl_c_len, c_len); c_errors++; break; }; if(!inode->sl) { inode->sl = (char *) malloc(PATH_MAX); if(!inode->sl) { perror("Can`t malloc: "); return -ENOMEM; }; }; if(c_separator) { inode->sl[inode->sl_len] = c_separator; inode->sl_len += 1; }; if(sl_c_flags & (1 << 1)) { // CURRENT component inode->sl[inode->sl_len] = '.'; inode->sl_len += 1; inode->sl[inode->sl_len] = '\0'; /* printf("parse_sa: SL CURRENT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n", sl_c_flags, sl_c_len, inode->sl_len, inode->sl);*/ } else if(sl_c_flags & (1 << 2)) { // PARENT component inode->sl[inode->sl_len] = '.'; inode->sl_len += 1; inode->sl[inode->sl_len] = '.'; inode->sl_len += 1; inode->sl[inode->sl_len] = '\0'; /* printf("parse_sa: SL PARENT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n", sl_c_len, inode->sl_len, inode->sl);*/ } else if(sl_c_flags & (1 << 3)) { // ROOT component (?! does not documented at all) inode->sl[inode->sl_len] = '/'; inode->sl_len += 1; inode->sl[inode->sl_len] = '\0'; /* printf("parse_sa: SL ROOT component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n", sl_c_len, inode->sl_len, inode->sl);*/ } else { strncpy(inode->sl + inode->sl_len, comp->text, sl_c_len); inode->sl_len += sl_c_len; inode->sl[inode->sl_len] = '\0'; /* printf("parse_sa: SL component, sl_c_flags %d, sl_c_len %d, sl_len %d, sl %s\n", sl_c_flags, sl_c_len, inode->sl_len, inode->sl);*/ }; // according to rrip, we must stop if CONTINUE flag isn`t set // according to linux kernel isofs code and images produced witj mkisofs // we need to continue while there is space in SL entry for next component c_offset += (2 + sl_c_len); // strict rrip code: // if(sl_c_flags & 1) { // CONTINUE // c_offset += 2 + sl_c_len; // } else { // printf("parse_sa: SL final component, sl_c_len %d, sl_c_flags %d, sl_len %d, sl %s\n", // sl_c_len, sl_c_flags, inode->sl_len, inode->sl); // break; // }; }; if(c_errors) { // printf("parse_sa: errors found while processing SL components, cleaning\n"); if(inode->sl) { free(inode->sl); }; inode->sl_len = 0; inode->SL = 0; } else if(!(sl_flags & 1) && inode->sl) { /* printf("parse_sa: SL entry (final), sue_len %d, sue_version %d, sl_len %d, sl %s\n", sue_len, sue_version, inode->sl_len, inode->sl);*/ inode->SL = 1; } else if(!(sl_flags & 1) && !inode->sl) { fprintf(stderr, "parse_sa: final SL entry found, but no SL components, cleaning\n"); if(inode->sl) { free(inode->sl); }; inode->sl_len = 0; inode->SL = 0; } else if(inode->sl) { /* printf("parse_sa: SL entry, sue_len %d, sue_version %d, sl_len %d, sl %s\n", sue_len, sue_version, inode->sl_len, inode->sl);*/ } else { fprintf(stderr, "parse_sa: empty SL entry?\n"); }; }; }; break; case SIG('N', 'M'): // rrip 4.1.4 if(sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect NM entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int nm_flags = sue->u.NM.flags; if(nm_flags & (1 << 1)) { // CURRENT bit /* printf("parse_sa: NM CURRENT entry, sue_version %d, sue_len %d, flags %d\n", sue_version, sue_len, nm_flags);*/ } else if(nm_flags & (1 << 2)) { // PARENT bit /* printf("parse_sa: NM PARENT entry, sue_version %d, sue_len %d, flags %d\n", sue_version, sue_len, nm_flags);*/ } else { if(sue_len - 5 + inode->nm_len > NAME_MAX - 1) { fprintf(stderr, "parse_sa: too long NM entry: %zu\n", sue_len - 5 + inode->nm_len); } else if(inode->NM) { fprintf(stderr, "parse_sa: NM entry already in effect, sue_len %d, sue_version %d\n", sue_len, sue_version); } else { if(!inode->nm) { inode->nm = (char *) malloc(NAME_MAX); if(!inode->nm) { perror("Can`t malloc: "); return -ENOMEM; }; }; strncpy(inode->nm + inode->nm_len, sue->u.NM.name, sue_len - 5); inode->nm_len += sue_len - 5; inode->nm[inode->nm_len] = '\0'; if(!nm_flags & 1) { // CONTINUE bit inode->NM = 1; /* printf("parse_sa: NM entry (final), flags %d, len %d, name %s\n", nm_flags, sue_len - 5, inode->nm);*/ } else { /* printf("parse_sa: NM entry (part), flags %d, len %d, name %s\n", nm_flags, sue_len - 5, inode->nm);*/ }; }; }; }; break; case SIG('C', 'L'): // rrip 4.1.5.1 if(sue_version != 1 || sue_len != 12) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect CL entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int cl_block = isonum_733(sue->u.CL.location); /* printf("parse_sa: CL entry, block %d\n", cl_block);*/ inode->CL = 1; inode->cl_block = cl_block; // read block pointed by CL record and process first directory entry char *buf = (char *) malloc(context.data_size); if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; ssize_t rc = isofs_read_raw_block(inode->cl_block, buf); if(rc < 0) { free(buf); return rc; }; struct iso_directory_record *record = (struct iso_directory_record *) buf; size_t record_length = isonum_711((unsigned char *) record->length); size_t name_len = isonum_711(record->name_len); size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len; /* printf("boff %d, record length %d, name_len %d, pad_len %d, sa_len %d\n", (int) boff, record_length, name_len, pad_len, sa_len);*/ if(record_length < sizeof(struct iso_directory_record)) { fprintf(stderr, "parse_sa: CL entry: directory record length too small: %zu\n", record_length); free(buf); return -EIO; }; if(name_len != 1) { fprintf(stderr, "parse_sa: file name length too big for . record: %zu\n", name_len); free(buf); return -EIO; }; if(record_length < name_len + sizeof(struct iso_directory_record) + pad_len) { // probably something wrong with name_len fprintf(stderr, "parse_sa: CL record: wrong name_len in directory entry: %zu, record_length %zu\n", name_len, record_length); free(buf); return -EIO; }; // ignoring anything from original record struct iso_directory_record *cl_record = (struct iso_directory_record *) malloc(sizeof(struct iso_directory_record)); if(!cl_record) { perror("Can`t malloc: "); return -ENOMEM; }; memcpy(cl_record, record, sizeof(struct iso_directory_record)); // drop existing record if(inode->record) { free(inode->record); }; // replace record with new one inode->record = cl_record; // parse sa entries from relocated directory . record rc = isofs_parse_sa(inode, ((char *) record) + sizeof(struct iso_directory_record) + name_len + pad_len + context.susp_skip, sa_len); if(rc) { free(buf); return rc; }; free(buf); }; break; case SIG('P', 'L'): // rrip 4.1.5.2 if(sue_version != 1 || sue_len != 12) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect PL entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int pl_block = isonum_733(sue->u.PL.location); /* printf("parse_sa: PL entry, block %d\n", pl_block);*/ // probably we don`t need process PL record with FUSE inode->PL = 1; inode->pl_block = pl_block; }; break; case SIG('R', 'E'): // rrip 4.1.5.3 if(sue_version != 1 || sue_len != 4) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect RE entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { // printf("parse_sa: RE entry\n"); inode->RE = 1; }; break; case SIG('T', 'F'): // rrip 4.1.6 if(sue_version != 1) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect TF entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { int tf_flags = sue->u.TF.flags; int stamp_length; if(tf_flags & TF_LONG_FORM) { // iso9660:8.4.26.1 format stamp_length = 17; } else { // iso9660:9.1.5 format stamp_length = 7; }; time_t ctime = 0; time_t mtime = 0; time_t atime = 0; int stamp_no = 0; // ctime can be stored as CREATION time if(tf_flags & TF_CREATE) { ctime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length); stamp_no++; }; if(tf_flags & TF_MODIFY) { mtime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length); stamp_no++; }; if(tf_flags & TF_ACCESS) { atime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length); stamp_no++; }; // ctime should be stored in ATTRIBUTES stamp according to rrip 4.1.6 if(tf_flags & TF_ATTRIBUTES) { ctime = isofs_date(((char *) sue) + 5 + stamp_length * stamp_no, stamp_length); stamp_no++; }; // other fields have no meaning for us /* printf("parse_sa: TF entry, sue_version %d, sue_len %d, ctime %d, mtime %d, atime %d\n", sue_version, sue_len, ctime, mtime, atime);*/ inode->st.st_ctime = ctime; inode->st.st_mtime = mtime; inode->st.st_atime = atime; inode->TF = 1; }; break; case SIG('S', 'F'): // rrip 4.1.7 if(sue_version != 1 || sue_len != 21) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect SF entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else { /// TODO does anyone support SF entries? linux isofs code does not.. fprintf(stderr, "parse_sa: SF entries (sparse files) are unsupported in this version, sorry..\n"); }; break; case SIG('Z', 'F'): // non-standard linux extension (zisofs) if(sue_version != 1 || sue_len != 16) { // incorrect entry, skip fprintf(stderr, "parse_sa: incorrect ZF entry: sue_len %d, sue_version %d\n", sue_len, sue_version); } else if(SIG(sue->u.ZF.algorithm[0], sue->u.ZF.algorithm[1]) == SIG('p', 'z')) { inode->zf_algorithm[0] = sue->u.ZF.algorithm[0]; inode->zf_algorithm[1] = sue->u.ZF.algorithm[1]; inode->zf_header_size = ((unsigned char) sue->u.ZF.parms[0]) << 2; inode->zf_block_shift = (unsigned char) sue->u.ZF.parms[1]; inode->zf_size = (inode->st.st_size ? inode->st.st_size : isonum_733(inode->record->size)); inode->real_size = isonum_733(sue->u.ZF.real_size); // check if file is really compressed, ignore ZF entry otherwise int rc = isofs_parse_zisofs_header(inode); if(rc == 0) { // printf("parse_sa: ZF entry found, algorithm %02x%02x, header size %d, block shift %d, compressed size %d, real size %d\n", // inode->zf_algorithm[0], inode->zf_algorithm[1], // inode->zf_header_size, inode->zf_block_shift, // inode->zf_size, inode->real_size); inode->ZF = 1; } else if(rc > 0) { fprintf(stderr, "parse_sa: ZF entry found, but file is not really compressed\n"); } else { // some error occur return rc; }; } else { fprintf(stderr, "parse_sa: unknown ZF compression algorithm %c%c, sorry..\n", sue->u.ZF.algorithm[0], sue->u.ZF.algorithm[1]); }; break; default: known_sue = 0; fprintf(stderr, "parse_sa: unknown entry '%c%c', sue_sig %d, sue_version %d, sue_len %d\n", sue->signature[0], sue->signature[1], sue_sig, sue_version, sue_len); break; }; if(sue_len >= 4 && known_sue) { remaining -= sue_len; } else if(known_sue) { fprintf(stderr, "parse_sa: inappropriate susp entry length: %d, signature %c%c\n", sue_len, sue->signature[0], sue->signature[1]); return -EIO; } else { // probably there are no more susp entries return 0; }; }; // process continuation area if found if(cont_block) { char *buf = (char *) malloc(context.data_size); if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; ssize_t rc = isofs_read_raw_block(cont_block, buf); if(rc < 0) { free(buf); return rc; }; /* printf("parse_sa: deep into CE, extent %d, offset %d, size %d\n", cont_block, cont_offset, cont_size);*/ rc = isofs_parse_sa(inode, buf + cont_offset, cont_size); if(rc) { free(buf); return rc; }; free(buf); }; return 0; }; int isofs_real_opendir(const char *path) { isofs_inode *inode = isofs_lookup(path); if(!inode) { fprintf(stderr, "opendir: know nothing about %s\n", path); return -ENOENT; }; if(!ISO_FLAGS_DIR(inode->record->flags)) { fprintf(stderr, "opendir: %s not a dir\n", path); return -ENOTDIR; }; return 0; }; int isofs_real_readdir(const char *path, void *filler_buf, fuse_fill_dir_t filler) { if(path[0] == '\0') { fprintf(stderr, "readdir: attempt to read empty path name\n"); return -EINVAL; }; isofs_inode *current_inode = isofs_lookup(path); if(!current_inode) { fprintf(stderr, "readdir: know nothing about %s\n", path); return -ENOENT; }; struct iso_directory_record *current = current_inode->record; if(!ISO_FLAGS_DIR(current->flags)) { fprintf(stderr, "readdir: %s not a dir\n", path); return -ENOTDIR; }; size_t current_size = isonum_733(current->size); int block = isonum_733(current->extent); char *buf = (char *) malloc(context.data_size); if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; ssize_t rc; // printf("path %s, current_size %d, block %d\n", path, current_size, block); size_t total_size = 0; int count = 1; int block_count = 0; off_t boff = 0; while(total_size <= current_size - sizeof(struct iso_directory_record)) { rc = isofs_read_raw_block(block, buf); if(rc < 0) { return rc; }; if((size_t)rc != context.data_size) { // can`t be allowed fprintf(stderr, "readdir: can`t read whole block, read only %zd bytes, block %d\n", rc, block); free(buf); return -EIO; }; block_count++; if(boff > 0) { total_size += (context.data_size - boff); boff = 0; }; while(boff + sizeof(struct iso_directory_record) <= context.data_size && total_size <= current_size - sizeof(struct iso_directory_record)) { struct iso_directory_record *record = (struct iso_directory_record *) (buf + boff); size_t record_length = isonum_711((unsigned char *) record->length); size_t name_len = isonum_711(record->name_len); size_t pad_len = ((name_len & 1) ? 0 : 1); // padding byte if name_len is even size_t sa_len = record_length - name_len - sizeof(struct iso_directory_record) - pad_len; // printf("block %d, boff %d, total_size %d, current_size %d, record length %d, name_len %d, pad_len %d, sa_len %d\n", // block, (int) boff, total_size, current_size, record_length, name_len, pad_len, sa_len); if(record_length == 0) { // possible end of directory or end of block total_size += (context.data_size - boff); boff = 0; break; }; if(record_length < sizeof(struct iso_directory_record)) { if(count > 2) { // check if . and .. is already read // possible end of directory // at least mkisofs does not set iso_directory_record.size correct // (this is much possible it was my fault and misunderstanding -- dmiceman) // but we need to try next block to be sure /// TODO this is not clear: what to do if next block not contain next directory? total_size += (context.data_size - boff); boff = 0; break; } else { fprintf(stderr, "readdir: directory record length too small: %zu\n", record_length); free(buf); return -EIO; }; }; if(name_len > NAME_MAX - 1) { fprintf(stderr, "readdir: file name length too big: %zu\n", name_len); free(buf); return -EIO; }; if(record_length < name_len + sizeof(struct iso_directory_record) + pad_len) { // probably something wrong with name_len fprintf(stderr, "readdir: wrong name_len in directory entry: %zu, record_length %zu\n", name_len, record_length); free(buf); return -EIO; }; if(count > 2 && name_len == 1 && record->name[0] == 0) { // looks like this is normal situation to meet another directory because // there is no clear way to find directory end // fprintf(stderr, "readdir: next directory found while processing another directory! boff %d, total_size %d, current_size %d, block %d, count %d\n", // (int) boff, total_size, current_size, block, count); goto out; }; isofs_inode *inode = (isofs_inode *) malloc(sizeof(isofs_inode)); if(!inode) { perror("Can`t malloc: "); return -ENOMEM; }; memset(inode, 0, sizeof(isofs_inode)); inode->st_ino = context.last_ino; context.last_ino++; struct iso_directory_record *n_rec = (struct iso_directory_record *) malloc (sizeof(struct iso_directory_record)); if(!n_rec) { perror("Can`t malloc: "); return -ENOMEM; }; memcpy(n_rec, record, sizeof(struct iso_directory_record)); inode->record = n_rec; if(context.susp || path[1] == '\0') { // if susp is known to be present or this is a root dir ("/") /* printf("sa offset %d, sa_len %d\n", sizeof(struct iso_directory_record) + name_len + pad_len, sa_len);*/ rc = isofs_parse_sa(inode, ((char *) record) + sizeof(struct iso_directory_record) + name_len + pad_len + context.susp_skip, sa_len); if(rc) { free(buf); isofs_free_inode(inode); return rc; }; }; char *entry = (char *) malloc(NAME_MAX); if(!entry) { perror("Can`t malloc: "); return -ENOMEM; }; if(count == 1) { // . entry ('\0' on disk) strcpy(entry, "."); } else if(count == 2) { // .. entry ('\1' on disk) strcpy(entry, ".."); } else { // regular entry if(inode->NM) { // rrip NM entry present and in effect // printf("readdir: NM entry is in effect\n"); strncpy(entry, inode->nm, inode->nm_len); entry[inode->nm_len] = '\0'; } else { // regular ISO9660 filename // printf("readdir: no NM entry found, name len %d\n", name_len); // because there can be '\0' characters because using of UCS-2 encoding we need to use memcpy memcpy(entry, (char *) record->name, name_len); entry[name_len] = '\0'; // fixup entry -- lowercase, strip leading ';', etc.. entry = isofs_fix_entry(entry, name_len); if(!entry) { fprintf(stderr, "readdir: error during entry fixup\n"); isofs_free_inode(inode); free(buf); return -EIO; }; }; }; // fprintf(stderr, "%d -- %s\n\n", count, entry); if(filler) { struct stat st; memset(& st, '\0', sizeof(struct stat)); isofs_direntry2stat(& st, inode); rc = filler(filler_buf, entry, & st, 0, 0); if(rc) { printf("readdir: filler return with %d, entry %s\n", (int)rc, entry); isofs_free_inode(inode); free(buf); free(entry); return -rc; }; }; char absolute_entry[PATH_MAX]; strlcpy(absolute_entry, path, PATH_MAX); if(path[1] != '\0') { // not root dir strlcat(absolute_entry, "/", PATH_MAX); }; if(strlcat(absolute_entry, entry, PATH_MAX) >= PATH_MAX) { fprintf(stderr, "readdir: absolute path name for entry '%s' exceeding PATH_MAX (%d)\n", entry, PATH_MAX); isofs_free_inode(inode); free(buf); free(entry); return -ENAMETOOLONG; }; if(g_hash_table_lookup(lookup_table, absolute_entry)) { // already in lookup cache isofs_free_inode(inode); } else { g_hash_table_insert(lookup_table, g_strdup(absolute_entry), inode); }; free(entry); boff += record_length; total_size += record_length; count++; }; // read next block block++; }; out: free(buf); return 0; }; int isofs_real_getattr(const char *path, struct stat *stbuf) { isofs_inode *inode = isofs_lookup(path); if(!inode) { // this error occur too often when browsing mounted tree with konqueror -- // it check existence of .directory file with stat() // fprintf(stderr, "getattr: know nothing about %s\n", path); return -ENOENT; } else { // printf("getattr: found %s, size %d\n", path, isonum_733(inode->record->size)); }; memset(stbuf, 0, sizeof(struct stat)); isofs_direntry2stat(stbuf, inode); /* if(ISO_FLAGS_DIR(inode->record->flags)) { printf("%s %i %i\n", path, (int) stbuf->st_size, stbuf->st_mode); };*/ return 0; }; int isofs_real_readlink(const char *path, char *target, size_t size) { isofs_inode *inode = isofs_lookup(path); if(!inode) { fprintf(stderr, "readlink: know nothing about %s\n", path); return -ENOENT; }; if(!inode->PX || !inode->SL || !S_ISLNK(inode->st.st_mode)) { fprintf(stderr, "readlink: %s not a link\n", path); return -EINVAL; }; strncpy(target, inode->sl, size - 1); target[size - 1] = '\0'; return 0; }; int isofs_real_open(const char *path) { isofs_inode *inode = isofs_lookup(path); if(!inode) { fprintf(stderr, "open: know nothing about %s\n", path); return -ENOENT; }; if(ISO_FLAGS_DIR(inode->record->flags)) { fprintf(stderr, "open: %s not a file\n", path); return -EINVAL; }; if(ISO_FLAGS_HIDDEN(inode->record->flags)) { fprintf(stderr, "open: %s is a hidden file\n", path); // return -EPERM; }; return 0; }; static ssize_t isofs_real_read_zf(isofs_inode *inode, char *out_buf, size_t size, off_t offset) { if(inode->zf_block_shift < 0 || inode->zf_block_shift > 30) { fprintf(stderr, "isofs_real_read_zf: invalid block shift %d\n", inode->zf_block_shift); return -EIO; } size_t zf_block_size = 1UL << inode->zf_block_shift; int zf_start = offset / zf_block_size; int zf_end = (offset + size) / zf_block_size; // protection against cornercase when read request is exactly equal to the uncompressed file size. // -- Ryan Thomas 2007-06-13 if( (offset + size ) % zf_block_size == 0 ) { zf_end--; }; int shift = offset % zf_block_size; // printf("zf_start %d, zf_end %d, size %d, offset %d, shift %d\n", // zf_start, zf_end, size, (int) offset, shift); // protection against some ununderstandable errors if(zf_start >= inode->zf_nblocks) { zf_start = inode->zf_nblocks - 1; }; if(zf_end >= inode->zf_nblocks) { zf_end = inode->zf_nblocks - 1; }; if(zf_end < 0 || zf_start < 0) { return 0; }; unsigned char *cbuf = (unsigned char *) malloc(zf_block_size * 2); if(!cbuf) { perror("Can`t malloc: "); return -ENOMEM; }; unsigned char *ubuf = (unsigned char *) malloc(zf_block_size); if(!ubuf) { perror("Can`t malloc: "); return -ENOMEM; }; size_t total_size = 0; size_t size_left = size; int base_offset = isonum_733(inode->record->extent) * context.data_size; int i; for(i = zf_start; i <= zf_end; i++) { unsigned int block_offset = isonum_731((char *) (& inode->zf_blockptr[i])); unsigned int block_end = isonum_731((char *) (& inode->zf_blockptr[i + 1])); unsigned int block_size = block_end - block_offset; if(block_size == 0) { // sort of sparse file block size_t payload_size = (zf_block_size - shift < size_left ? zf_block_size - shift: size_left); memset(out_buf + total_size, 0, payload_size); total_size += payload_size; size_left -= payload_size; } else if(block_size > zf_block_size * 2) { fprintf(stderr, "isofs_real_read_zf: compressed block size bigger than uncompressed block size * 2, something is wrong there.. block size %u, uncompressed block size %zu\n", block_size, zf_block_size); free(cbuf); free(ubuf); return -EIO; } else { // we do not use isofs_read_raw_block() there because it is simpler to access // compressed blocks directly unsigned int image_off = base_offset + block_offset; if(pthread_mutex_lock(& fd_mutex)) { int err = errno; perror("isofs_real_read_zf: can`l lock fd_mutex"); free(cbuf); free(ubuf); return -err; }; if(lseek(context.fd, image_off, SEEK_SET) == -1) { perror("isofs_real_read_zf: can`t lseek()"); pthread_mutex_unlock(& fd_mutex); free(cbuf); free(ubuf); return -EIO; }; size_t len = read(context.fd, cbuf, block_size); if(len != block_size) { fprintf(stderr, "isofs_real_read_zf: can`t read full block, errno %d, message %s\n", errno, strerror(errno)); pthread_mutex_unlock(& fd_mutex); free(cbuf); free(ubuf); return -EIO; }; pthread_mutex_unlock(& fd_mutex); // compressed block is read from disk, now uncompress() it uLongf usize = zf_block_size; int rc = uncompress(ubuf, &usize, cbuf, block_size); if(rc != Z_OK) { free(cbuf); free(ubuf); fprintf(stderr, "isofs_real_read_zf: uncompress() error %i: %s\n", rc, strerror(rc)); return -rc; }; // ubuf contain uncompressed data, usize contain uncompressed block size size_t payload_size = (usize - shift < size_left ? usize - shift : size_left); memcpy(out_buf + total_size, ubuf + shift, payload_size); total_size += payload_size; size_left -= payload_size; }; // shift is only meaningful for first block shift = 0; }; free(cbuf); free(ubuf); // printf("total size %d\n", total_size); return total_size; }; ssize_t isofs_real_read(const char *path, char *out_buf, size_t size, off_t offset) { isofs_inode *inode = isofs_lookup(path); if(!inode) { fprintf(stderr, "read: know nothing about %s\n", path); return -ENOENT; }; struct iso_directory_record *record = inode->record; if(ISO_FLAGS_DIR(record->flags)) { fprintf(stderr, "read: %s not a file\n", path); return -EINVAL; }; if(ISO_FLAGS_HIDDEN(record->flags)) { fprintf(stderr, "read: %s is a hidden file\n", path); // return -EPERM; }; if(inode->ZF) { // this file is compressed, handle it specially return isofs_real_read_zf(inode, out_buf, size, offset); }; size_t fsize = isonum_733(record->size); if(offset + size > fsize) { size = fsize - offset; }; if(size < 1) { return 0; }; int start_block = isonum_733(record->extent); if(start_block == 0) { // empty file return 0; }; int start = offset / context.data_size; int end = (offset + size) / context.data_size; int shift = offset % context.data_size; int block = start_block + start; // printf("read: path %s, size %d, offset %d, fsize %d, start %d, end %d, shift %d\n", // path, size, (int) offset, fsize, start, end, shift); char *buf = (char *) malloc(context.data_size); if(!buf) { perror("Can`t malloc: "); return -ENOMEM; }; int i; size_t total_size = 0; size_t size_left = size; int count = 0; for(i = start; i <= end; i++) { ssize_t len = isofs_read_raw_block(block, buf); if(len < 0) { free(buf); return len; }; // printf("read: block %d, len %d, size_left %d\n", block, len, size_left); if((size_t)len > size_left) { len = size_left; }; total_size += len; memcpy(out_buf + count * context.data_size, buf + shift, len - shift); count++; shift = 0; size_left -= len; block++; }; free(buf); return total_size; }; int isofs_real_statfs(struct statvfs *stbuf) { stbuf->f_type = ISOFS_SUPER_MAGIC; stbuf->f_bsize = context.data_size; // or PAGE_CACHE_SIZE? stbuf->f_blocks = 0; // while it is possible to calculate this, i see no reasons to do so stbuf->f_bfree = 0; stbuf->f_bavail = 0; stbuf->f_files = 0; stbuf->f_ffree = 0; stbuf->f_favail = 0; stbuf->f_namemax = NAME_MAX - 1; // ? not sure.. return 0; }; fuseiso-v20260614/src/isofs.h000066400000000000000000000125351521353471600157230ustar00rootroot00000000000000/*************************************************************************** * Copyright (C) 2005, 2006 by Dmitry Morozhnikov * * dmiceman@mail.ru * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef _ISOFS_H #define _ISOFS_H #include #include #include #define FUSE_USE_VERSION 314 #include typedef struct _isofs_context { char *imagefile; int fd; int pd_have_rr; // 1 if primary descriptor have hierarchy with rrip extension struct iso_primary_descriptor pd; int supplementary; // 1 if supplementary descriptor found and in effect struct iso_supplementary_descriptor sd; struct iso_directory_record *root; int file_offset; // offset to begin of useful data (for .nrg files) off_t id_offset; // offset to CD001 inside file size_t block_size; // raw block size size_t block_offset; // offset from block start to data size_t data_size; // data size inside block int susp; // parse susp entries int susp_skip; // skip bytes from susp SP entry int joliet_level; // joliet extension level (1, 2 or 3) ino_t last_ino; } isofs_context; typedef struct _isofs_inode { struct iso_directory_record *record; struct stat st; ino_t st_ino; time_t ctime; // cached value from record->date char *sl; size_t sl_len; char *nm; size_t nm_len; int cl_block; int pl_block; off_t zf_size; off_t real_size; char zf_algorithm[2]; size_t zf_header_size; int zf_block_shift; int zf_nblocks; int *zf_blockptr; int PX; // 1 if PX entry found, st in effect int SL; // 1 if SL entry found, sl in effect int NM; // 1 if NM entry found, nm in effect int CL; // 1 if CL found, cl_block in effect int PL; // 1 if PL found, pl_block in effect int RE; int TF; // 1 if TF entry found, st in effect int ZF; // 1 if ZF entry found } isofs_inode; // borrowed from zisofs-tools typedef struct _zf_file_header { char magic[8]; char uncompressed_len[4]; unsigned char header_size; unsigned char block_size; char reserved[2]; } zf_file_header; // macros for iso_directory_record->flags #define ISO_FLAGS_HIDDEN(x) (*((unsigned char *) x) & 1) #define ISO_FLAGS_DIR(x) (*((unsigned char *) x) & (1 << 1)) // borrowed from linux kernel rock ridge code #define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */ // borrowed from linux kernel isofs code /* Number conversion inlines, named after the section in ISO 9660 they correspond to. */ #include static inline int isonum_711(unsigned char *p) { return *(unsigned char *)p; } // static inline int isonum_712(char *p) // { // return *(s8 *)p; // } static inline unsigned int isonum_721(char *p) { #if defined(WORDS_BIGENDIAN) return *(unsigned short *)p; #else return bswap_16(*(unsigned short *)p); #endif } // static inline unsigned int isonum_722(char *p) // { // return be16_to_cpu(get_unaligned((__le16 *)p)); // } static inline unsigned int isonum_723(char *p) { /* Ignore bigendian datum due to broken mastering programs */ #if defined(WORDS_BIGENDIAN) return bswap_16(*(unsigned short *)p); #else return *(unsigned short *)p; #endif } static inline unsigned int isonum_731(char *p) { #if defined(WORDS_BIGENDIAN) return bswap_32(*(unsigned int *)p); #else return *(unsigned int *)p; #endif } // static inline unsigned int isonum_732(char *p) // { // return be32_to_cpu(get_unaligned((__le32 *)p)); // } static inline unsigned int isonum_733(char *p) { /* Ignore bigendian datum due to broken mastering programs */ #if defined(WORDS_BIGENDIAN) return bswap_32(*(unsigned int *)p); #else return *(unsigned int *)p; #endif } int isofs_real_preinit(char* imagefile, int fd); void* isofs_real_init(); int isofs_real_opendir(const char *path); int isofs_real_readdir(const char *path, void *filler_buf, fuse_fill_dir_t filler); int isofs_real_getattr(const char *path, struct stat *stbuf); int isofs_real_readlink(const char *path, char *target, size_t size); int isofs_real_open(const char *path); ssize_t isofs_real_read(const char *path, char *out_buf, size_t size, off_t offset); int isofs_real_statfs(struct statvfs *stbuf); #endif // _ISOFS_H fuseiso-v20260614/src/meson.build000066400000000000000000000003611521353471600165630ustar00rootroot00000000000000fuseiso = executable('fuseiso', 'fuseiso.c', 'isofs.c', dependencies: [fuse_dep, glib_dep, zlib_dep], c_args: ['-D_FILE_OFFSET_BITS=64'], include_directories: include_directories('..'), build_by_default: true, install: true, ) fuseiso-v20260614/zAppRun/000077500000000000000000000000001521353471600152315ustar00rootroot00000000000000fuseiso-v20260614/zAppRun/zAppRun000077500000000000000000000142731521353471600165650ustar00rootroot00000000000000#!/bin/bash # by probono at myrealbox dot com # thanks to bfree # 2005-09-22 converted to use fusecram by dmiceman@mail.ru # 2005-10-29 added support for fuseiso by dmiceman@mail.ru # GPL export PATH=/sbin:$PATH # Fedora needs this # # ok we need dialogs now # if [ -z "$DIALOG" ] ; then # Determine which dialog to use in which situation: # Xdialog (in all other cases) export DIALOG=Xdialog # kdialog (in case there is no console available and we are running KDE) pidof kdesktop >/dev/null && export DIALOG=kdialog pidof gnome-panel > /dev/null && export DIALOG=zenity # dialog (in case there is a console available) GUIMODE=$(tty) ( echo $GUIMODE | grep /dev/tty[:digit:] >/dev/null ) && export DIALOG=dialog fi # Setup defaults for whatever dialog we are using case $DIALOG in kdialog) DIALOG_OPTIONS=" --caption klik" ; KLIKDIR=":klikdir" ;; Xdialog|dialog) DIALOG_H=12 DIALOG_W=60 DIALOG_OPTIONS=" $DIALOG_H $DIALOG_W" ; KLIKDIR="~" ;; esac derror(){ case $DIALOG in zenity) $DIALOG --error --text "$1" --title="klik" ;; kdialog) $DIALOG --error "$1" $DIALOG_OPTIONS ;; Xdialog|dialog) $DIALOG --msgbox "ERROR: $1" $DIALOG_OPTIONS ;; esac } function check_fstab() { # check fstab and warn if neccessary entries are not there # better use /media/klik according to FSH? if [ -z "$(cat /etc/fstab | grep app/7)" ] then derror "Your /etc/fstab is not yet prepared for mounting .cmg images. As root, please make /tmp/app writeable and add the following lines: ################################################################ /tmp/app/1/image /tmp/app/1 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/2/image /tmp/app/2 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/3/image /tmp/app/3 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/4/image /tmp/app/4 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/5/image /tmp/app/5 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/6/image /tmp/app/6 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 /tmp/app/7/image /tmp/app/7 cramfs,iso9660 user,noauto,ro,loop,exec 0 0 ################################################################" exit 1 fi } # rewrite cmdline to use absolute instead of relative paths, thanks bfree NEWCMD=$(perl -e '$newcmd=shift(@ARGV);foreach $arg (@ARGV){ @part=split(/\=/,$arg); foreach $part (@part){ (-e "$ENV{PWD}/$part") && ($part="$ENV{PWD}/$part");}$newcmd.=" ".join ("=",@part);} print "$newcmd";' $@) set -- $NEWCMD # if no arguments are passed and # there is a .cmg in the same directory as this # script, then use the .cmg DIRNAME=$(dirname $0) if [ -z $1 ] then CMG=$(find "$DIRNAME" -iname '*.cmg'|head -n 1) || exit 1 echo "Found $CMG, using it" else CMG="$1" shift fi # make path to CMG absolute, thanks bfree case $CMG in /*) ;; *) CMG=$(pwd)/$CMG ;; esac # determine which filesystem is used as .cmg file "$CMG" | grep ": data" >/dev/null && FS=squash # who knows a better way to recognize it? file "$CMG" | grep "Compressed ROM" >/dev/null && FS=cram file "$CMG" | grep "ISO 9660" >/dev/null && FS=iso # sometimes file does not recognize iso9660 images, try hard way if [ -z $FS ] ; then dd if="$CMG" skip=32769 bs=1 count=5 2> /dev/null | grep CD001 >/dev/null && FS=iso fi if [ -n "$FS" ] then NUMBERS="7 6 5 4 3 2 1" for NUMBER in $NUMBERS do [ -e "/tmp/app/$NUMBER" ] || MNTNUM=$NUMBER done case $FS in squash) MOUNT=/tmp/squash/$MNTNUM mkdir -p $MOUNT || exit 1 ln -s $CMG $MOUNT/image || exit 1 mount $MOUNT || derror "Unable to mount $MOUNT" ;; cram) FUSECRAM=`which fusecram 2> /dev/null` MOUNT=/tmp/app/$MNTNUM mkdir -p $MOUNT || exit 1 if [ -z "$FUSECRAM" ] ; then check_fstab ln -s $CMG $MOUNT/image || exit 1 mount $MOUNT/image || derror "Unable to mount $MOUNT" else # using fusecram -- dmiceman $FUSECRAM $CMG $MOUNT || derror "Unable to mount $MOUNT: $?" fi ;; iso) FUSEISO=`which fuseiso 2> /dev/null` MOUNT=/tmp/app/$MNTNUM mkdir -p $MOUNT || exit 1 if [ -z "$FUSEISO" ] ; then check_fstab ln -s $CMG $MOUNT/image || exit 1 mount $MOUNT/image || derror "Unable to mount $MOUNT" else # using fuseiso -- dmiceman $FUSEISO $CMG $MOUNT || derror "Unable to mount $MOUNT: $?" fi ;; *) MOUNT=/tmp/app/$MNTNUM mkdir -p $MOUNT || exit 1 ln -s $CMG $MOUNT/image || exit 1 mount $MOUNT || derror "Unable to mount $MOUNT" ;; esac else # NOTE: exit now cause our cmg isn't mounted derror "$CMG does not appear to be either a squashfs, iso9660 or a cramfs file" exit 1 fi # # execute the wrapper # the wrapper should take care to keep running until its app closes # # we need this so that on the cmdline, pipes etc work CMDLINE="yes" ( tty | grep ^/dev/tty >/dev/null ) && CMDLINE="" ( tty | grep ^/dev/pts >/dev/null ) && CMDLINE="" if [ "$CMDLINE" = "yes" ] ; then RESULT=$($MOUNT/wrapper $@ 2>&1) || derror "$RESULT" else $MOUNT/wrapper $@ fi # kill all child processes # kill $(pidof -x -o %PPID $!) # 2>/dev/null # unmount and clean up if [ -n "$FUSECRAM" -o -n "$FUSEISO" ] ; then # umount want not to umount FUSE mounts -- dmiceman fusermount -u $MOUNT rm -r $MOUNT/ else umount $MOUNT rm -f $MOUNT/image rm -r $MOUNT/ fi ################## # update klik menu CMGDIR=$(dirname $CMG) [ x"$KDEHOME" = x ] && KDEHOME=$HOME/.kde # find cmg files CMGFILES=$(find $CMGDIR/*.cmg 2>/dev/null) # remove old menu entries rm -rf ${KDEHOME}/share/applnk/klik/*.cmg.desktop # create new menu entries for CMGFILE in $CMGFILES do #echo $CMGFILE BASENAME=$(basename "$CMGFILE") APPNAME=$(echo $BASENAME | sed 's/.cmg//g' | cut -d _ -f 1) firstchar=${APPNAME:0:1} # First character. restchar=${APPNAME:1} # Rest of string(s). firstchar=$(echo "$firstchar" | tr a-z A-Z) APPNAME=$firstchar$restchar cat > $KDEHOME/share/applnk/klik/$BASENAME.desktop <