diff options
Diffstat (limited to 'src/libs/uio')
52 files changed, 15441 insertions, 0 deletions
diff --git a/src/libs/uio/COPYING b/src/libs/uio/COPYING new file mode 100644 index 0000000..2774fef --- /dev/null +++ b/src/libs/uio/COPYING @@ -0,0 +1,350 @@ +Below the text of the GNU General Public License is quoted verbatim. +It applies to all files in and below this directory that say so. +Note that most of these files will say only version 2 of the +GNU General Public License applies, not any later version. + + +--------------------------------------------------------------------------- + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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. + + diff --git a/src/libs/uio/Makeinfo b/src/libs/uio/Makeinfo new file mode 100644 index 0000000..9d127fc --- /dev/null +++ b/src/libs/uio/Makeinfo @@ -0,0 +1,22 @@ +uqm_SUBDIRS="stdio" +uqm_CFILES="charhashtable.c defaultfs.c fileblock.c fstypes.c gphys.c io.c + ioaux.c match.c mount.c mounttree.c paths.c physical.c uiostream.c + uioutils.c utils.c" +uqm_HFILES="charhashtable.h defaultfs.h fileblock.h fstypes.h getint.h + gphys.h ioaux.h io.h iointrn.h match.h mem.h mount.h mounttree.h + paths.h physical.h types.h uioport.h uiostream.h uioutils.h utils.h" + +if [ -n "$uqm_USE_ZIP_IO" ]; then + uqm_SUBDIRS="$uqm_SUBDIRS zip" +fi + +#if [ -n "$DEBUG" -o -n "$uqm_UIO_DEBUG" ]; then + uqm_CFILES="$uqm_CFILES debug.c" + uqm_HFILES="$uqm_HFILES debug.h" +#fi + +if [ -n "$MEMDEBUG" ]; then + uqm_CFILES="$uqm_CFILES hashtable.c memdebug.c" + uqm_HFILES="$uqm_HFILES hashtable.h memdebug.h" +fi + diff --git a/src/libs/uio/charhashtable.c b/src/libs/uio/charhashtable.c new file mode 100644 index 0000000..df10b2a --- /dev/null +++ b/src/libs/uio/charhashtable.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + + +#define HASHTABLE_INTERNAL +#include "charhashtable.h" +#include "types.h" +#include "uioport.h" + +static inline uio_uint32 CharHashTable_hash(CharHashTable_HashTable *hashTable, + const char *string); +static inline uio_bool CharHashTable_equal(CharHashTable_HashTable *hashTable, + const char *key1, const char *key2); +static inline char *CharHashTable_copy(CharHashTable_HashTable *hashTable, + const char *key); +static inline void CharHashTable_freeKey(CharHashTable_HashTable *hashTable, + char *key); + +#include "hashtable.c" + + +static inline uio_uint32 +CharHashTable_hash(CharHashTable_HashTable *hashTable, const char *key) { + uio_uint32 hash; + + (void) hashTable; + // Rotating hash, variation of something on the web which + // wasn't original itself. + hash = 0; + // Hash was on that web page initialised as the length, + // but that isn't known at this time. + while (*key != '\0') { + hash = (hash << 4) ^ (hash >> 28) ^ *key; + key++; + } + return hash ^ (hash >> 10) ^ (hash >> 20); +} + +static inline uio_bool +CharHashTable_equal(CharHashTable_HashTable *hashTable, + const char *key1, const char *key2) { + (void) hashTable; + return strcmp(key1, key2) == 0; +} + +static inline char * +CharHashTable_copy(CharHashTable_HashTable *hashTable, + const char *key) { + (void) hashTable; + return uio_strdup(key); +} + +static inline void +CharHashTable_freeKey(CharHashTable_HashTable *hashTable, + char *key) { + (void) hashTable; + uio_free(key); +} + + diff --git a/src/libs/uio/charhashtable.h b/src/libs/uio/charhashtable.h new file mode 100644 index 0000000..3cd0add --- /dev/null +++ b/src/libs/uio/charhashtable.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_CHARHASHTABLE_H_ +#define LIBS_UIO_CHARHASHTABLE_H_ + + +#define HASHTABLE_(identifier) CharHashTable ## _ ## identifier +typedef char HASHTABLE_(Key); +typedef void HASHTABLE_(Value); +#define CharHashTable_HASH CharHashTable_hash +#define CharHashTable_EQUAL CharHashTable_equal +#define CharHashTable_COPY CharHashTable_copy +#define CharHashTable_FREEKEY CharHashTable_freeKey +#define CharHashTable_FREEVALUE(hashTable, value) \ + ((void) (hashTable), (void) (value)) + +#include "hashtable.h" + + +#endif /* _CHARHASHTABLE_H */ + diff --git a/src/libs/uio/debug.c b/src/libs/uio/debug.c new file mode 100644 index 0000000..66bab47 --- /dev/null +++ b/src/libs/uio/debug.c @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#if defined(__unix__) && !defined(_WIN32_WCE) +# include <sys/wait.h> +#endif + +#include "debug.h" +#include "uioport.h" +#include "io.h" +#include "utils.h" +#include "types.h" +#include "mem.h" +#include "uioutils.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +#define LINEBUFLEN 1024 + +typedef struct DebugContext { + uio_bool exit; + FILE *in; + FILE *out; + FILE *err; + uio_DirHandle *cwd; +} DebugContext; + +typedef struct { + const char *name; + int (*fun)(DebugContext *, int, char *[]); +} DebugCommand; + +#ifdef STAND_ALONE +void initRepository(void); +void unInitRepository(void); +#endif + +static int debugCmdCat(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdCd(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdExec(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdExit(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdLs(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdMem(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdMount(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdMv(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdRm(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdStat(DebugContext *debugContext, int argc, char *argv[]); +static int debugCmdWriteTest(DebugContext *debugContext, int argc, + char *argv[]); +static int debugCmdFwriteTest(DebugContext *debugContext, int argc, + char *argv[]); + +static void makeArgs(char *lineBuf, int *argc, char ***argv); +static int debugCallCommand(DebugContext *debugContext, + int argc, char *argv[]); +uio_MountHandle * +debugMountOne(uio_Repository *destRep, const char *mountPoint, + uio_FileSystemID fsType, + uio_DirHandle *sourceDir, const char *sourcePath, + const char *inPath, uio_AutoMount **autoMount, int flags, + uio_MountHandle *relative); + +// In alphabetic order: +DebugCommand debugCommands[] = { + { "cat", debugCmdCat }, + { "cd", debugCmdCd }, + { "exec", debugCmdExec }, + { "exit", debugCmdExit }, + { "fwritetest", debugCmdFwriteTest }, + { "ls", debugCmdLs }, + { "mem", debugCmdMem }, + { "mkdir", debugCmdMkDir }, + { "mount", debugCmdMount }, + { "mv", debugCmdMv }, + { "pwd", debugCmdPwd }, + { "rm", debugCmdRm }, + { "rmdir", debugCmdRmDir }, + { "stat", debugCmdStat }, + { "writetest", debugCmdWriteTest }, +}; + + +#ifndef STAND_ALONE +extern uio_Repository *repository; +#else +uio_Repository *repository; +uio_MountHandle *mountHandles[9]; // TODO: remove (this is just a test) + +int +main(int argc, char *argv[]) { + initRepository(); + uio_debugInteractive(stdin, stdout, stderr); + unInitRepository(); + (void) argc; + (void) argv; + return EXIT_SUCCESS; +} + +uio_MountHandle * +debugMountOne(uio_Repository *destRep, const char *mountPoint, + uio_FileSystemID fsType, + uio_DirHandle *sourceDir, const char *sourcePath, + const char *inPath, uio_AutoMount **autoMount, int flags, + uio_MountHandle *relative) { + uio_MountHandle *mountHandle; + + mountHandle = uio_mountDir(destRep, mountPoint, fsType, sourceDir, + sourcePath, inPath, autoMount, flags, relative); + if (mountHandle == NULL) { + int savedErrno = errno; + fprintf(stderr, "Could not mount '%s' and graft '%s' from that " + "into the repository at '%s': %s\n", + sourcePath, inPath, mountPoint, strerror(errno)); + errno = savedErrno; + } + return mountHandle; +} + +void +initRepository(void) { + static uio_AutoMount autoMountZip = { + .pattern = "*.zip", + .matchType = match_MATCH_SUFFIX, + .fileSystemID = uio_FSTYPE_ZIP, + .mountFlags = uio_MOUNT_BELOW | uio_MOUNT_RDONLY + }; + static uio_AutoMount *autoMount[] = { + &autoMountZip, + NULL + }; + + uio_init(); + repository = uio_openRepository(0); + + memset(&mountHandles, '\0', sizeof mountHandles); +#if 1 + mountHandles[0] = debugMountOne(repository, "/", uio_FSTYPE_STDIO, + NULL, NULL, "/home/svdb/cvs/sc2/content", autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif +#if 1 + mountHandles[1] = debugMountOne(repository, "/", uio_FSTYPE_STDIO, + NULL, NULL, "/home/svdb/cvs/sc2/src/sc2code/ships", + autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif +#if 1 + mountHandles[2] = debugMountOne(repository, "/", uio_FSTYPE_STDIO, + NULL, NULL, "/tmp/vfstest", autoMount, uio_MOUNT_TOP, NULL); +#endif +#if 1 + mountHandles[3] = debugMountOne(repository, "/", uio_FSTYPE_STDIO, + NULL, NULL, "/tmp/vfstest2", autoMount, uio_MOUNT_TOP, NULL); +#endif + + // TODO: should work too: +#if 0 + mountHandle[4] = debugMountOne(repository, "/zip/", uio_FSTYPE_ZIP, NULL, + NULL, "/ziptest/foo.zip", autoMount, uio_MOUNT_TOP, NULL); +#endif + { + uio_DirHandle *rootDir; + rootDir = uio_openDir(repository, "/", 0); + if (rootDir == NULL) { + fprintf(stderr, "Could not open '/' dir.\n"); + } else { +#if 1 + mountHandles[4] = debugMountOne(repository, "/example/", + uio_FSTYPE_ZIP, rootDir, "/example2.zip", "/", autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif +#if 1 + mountHandles[5] = debugMountOne(repository, "/example/", + uio_FSTYPE_ZIP, rootDir, "/example/example.zip", "/", + autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif +#if 1 + mountHandles[6] = debugMountOne(repository, "/zip/", + uio_FSTYPE_ZIP, rootDir, "/voice.zip", "/", autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif +#if 1 + mountHandles[7] = debugMountOne(repository, "/foo/", + uio_FSTYPE_ZIP, rootDir, "/foo2.zip", "/", autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif + uio_closeDir(rootDir); + } + } + mountHandles[8] = debugMountOne(repository, "/tmp/", + uio_FSTYPE_STDIO, NULL, NULL, "/tmp/", autoMount, + uio_MOUNT_TOP, NULL); + +#if 1 + mountHandles[8] = debugMountOne(repository, "/root/root/", + uio_FSTYPE_STDIO, NULL, NULL, "/", autoMount, + uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); +#endif + +} + +void +unInitRepository(void) { +#if 1 + int i; +// uio_printMountTree(stderr, repository->mountTree, 0); +// fprintf(stderr, "\n"); + for (i = 7; i >= 0; i--) { + if (mountHandles[i] != NULL) + uio_unmountDir(mountHandles[i]); +// uio_printMountTree(stderr, repository->mountTree, 0); +// uio_printMounts(stderr, repository); +// fprintf(stderr, "\n"); + } +#endif + uio_closeRepository(repository); + uio_unInit(); +} +#endif /* STAND_ALONE */ + +void +uio_debugInteractive(FILE *in, FILE *out, FILE *err) { + char lineBuf[LINEBUFLEN]; + size_t lineLen; + int argc; + char **argv; + DebugContext debugContext; + uio_bool interactive; + + memset(&debugContext, '\0', sizeof (DebugContext)); + debugContext.exit = false; + debugContext.in = in; + debugContext.out = out; + debugContext.err = err; + debugContext.cwd = uio_openDir(repository, "/", 0); + if (debugContext.cwd == NULL) { + fprintf(err, "Fatal: Could not open working dir.\n"); + abort(); + } + + interactive = isatty(fileno(in)); + do { + if (interactive) + fprintf(out, "> "); + if (fgets(lineBuf, LINEBUFLEN, in) == NULL) { + if (feof(in)) { + // user pressed ^D + break; + } + // error occured + clearerr(in); + continue; + } + lineLen = strlen(lineBuf); + if (lineBuf[lineLen - 1] != '\n' && lineBuf[lineLen - 1] != '\r') { + fprintf(err, "Too long command line.\n"); + // TODO: read until EOL + continue; + } + makeArgs(lineBuf, &argc, &argv); + if (argc == 0) { + uio_free(argv); + continue; + } + debugCallCommand(&debugContext, argc, argv); + uio_free(argv); + } while (!debugContext.exit); + if (interactive) + fprintf(out, "\n"); + uio_closeDir(debugContext.cwd); +} + +static void +makeArgs(char *lineBuf, int *argc, char ***argv) { + int numArg; + char **args; + char *ptr; + + numArg = 0; + ptr = lineBuf; + while(true) { + while (isspace((int) *ptr)) + ptr++; + if (*ptr == '\0') + break; + numArg++; + while (!isspace((int) *ptr)) + ptr++; + } + + args = uio_malloc((numArg + 1) * sizeof (char *)); + numArg = 0; + ptr = lineBuf; + while(true) { + while (isspace((int) *ptr)) + ptr++; + if (*ptr == '\0') + break; + args[numArg] = ptr; + numArg++; + while (!isspace((int) *ptr)) + ptr++; + if (*ptr == '\0') + break; + *ptr = '\0'; + ptr++; + } + args[numArg] = NULL; + *argv = args; + *argc = numArg; +} + +static int +debugCallCommand(DebugContext *debugContext, int argc, char *argv[]) { + int i; + int numDebugCommands; + + i = 0; + numDebugCommands = sizeof debugCommands / sizeof debugCommands[0]; + // could be improved with binary search + while(1) { + if (i == numDebugCommands) { + fprintf(debugContext->err, "Invalid command.\n"); + return 1; + } + if (strcmp(argv[0], debugCommands[i].name) == 0) + break; + i++; + } + return debugCommands[i].fun(debugContext, argc, argv); +} + +static int +debugCmdCat(DebugContext *debugContext, int argc, char *argv[]) { + uio_Handle *handle; +#define READBUFSIZE 0x10000 + char readBuf[READBUFSIZE]; + char *bufPtr; + ssize_t numInBuf, numWritten; + size_t totalWritten; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + handle = uio_open(debugContext->cwd, argv[1], O_RDONLY +#ifdef WIN32 + | O_BINARY +#endif + , 0); + if (handle == NULL) { + fprintf(debugContext->err, "Could not open file: %s\n", + strerror(errno)); + return 1; + } + + totalWritten = 0; + while (1) { + numInBuf = uio_read(handle, readBuf, READBUFSIZE); + if (numInBuf == -1) { + if (errno == EINTR) + continue; + fprintf(debugContext->err, "Could not read from file: %s\n", + strerror(errno)); + uio_close(handle); + return 1; + } + if (numInBuf == 0) + break; + bufPtr = readBuf; + do { + numWritten = write(fileno(debugContext->out), bufPtr, numInBuf); + if (numWritten == -1) + { + if (errno == EINTR) + continue; + fprintf(debugContext->err, "Could not read from file: %s\n", + strerror(errno)); + uio_close(handle); + } + numInBuf -= numWritten; + bufPtr += numWritten; + totalWritten += numWritten; + } while (numInBuf > 0); + } + fprintf(debugContext->out, "[%u bytes]\n", (unsigned int) totalWritten); + + uio_close(handle); + return 0; +} + +static int +debugCmdCd(DebugContext *debugContext, int argc, char *argv[]) { + uio_DirHandle *newWd; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + newWd = uio_openDirRelative(debugContext->cwd, argv[1], 0); + if (newWd == NULL) { + fprintf(debugContext->err, "Could not access new dir: %s\n", + strerror(errno)); + return 1; + } + uio_closeDir(debugContext->cwd); + debugContext->cwd = newWd; + return 0; +} + +static int +debugCmdExec(DebugContext *debugContext, int argc, char *argv[]) { + int i; + const char **newArgs; + int errCode = 0; + uio_StdioAccessHandle **handles; + uio_DirHandle *tempDir; + + if (argc < 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + tempDir = uio_openDirRelative(debugContext->cwd, "/tmp", 0); + if (tempDir == 0) { + fprintf(debugContext->err, "Could not open temp dir: %s.\n", + strerror(errno)); + return 1; + } + + newArgs = uio_malloc(argc * sizeof (char *)); + newArgs[0] = argv[1]; + handles = uio_malloc(argc * sizeof (uio_StdioAccessHandle *)); + handles[0] = NULL; + + for (i = 2; i < argc; i++) { +#if 0 + if (argv[i][0] == '-') { + // Don't try to parse arguments that start with '-'. + // They are probably option flags. + newArgs[i - 1] = argv[i]; + } +#endif + handles[i - 1] = uio_getStdioAccess(debugContext->cwd, argv[i], + O_RDONLY, tempDir); + if (handles[i - 1] == NULL) { + if (errno == ENOENT) { + // No match; we keep what's typed literally. + newArgs[i - 1] = argv[i]; + continue; + } + + // error + fprintf(debugContext->err, + "Cannot execute: Cannot get stdio access to %s: %s.\n", + argv[i], strerror(errno)); + errCode = 1; + argc = i + 1; + goto err; + } + + newArgs[i - 1] = uio_StdioAccessHandle_getPath(handles[i - 1]); + } + newArgs[argc - 1] = NULL; + + fprintf(debugContext->err, "Executing: %s", newArgs[0]); + for (i = 1; i < argc - 1; i++) + fprintf(debugContext->err, " %s", newArgs[i]); + fprintf(debugContext->err, "\n"); + +#if defined(__unix__) && !defined(_WIN32_WCE) + { + pid_t pid; + + pid = fork(); + switch (pid) { + case -1: + fprintf(debugContext->err, "Error: fork() failed: %s.\n", + strerror(errno)); + break; + case 0: + // child + execvp(newArgs[0], (char * const *) newArgs); + fprintf(debugContext->err, "Error: execvp() failed: %s.\n", + strerror(errno)); + _exit(EXIT_FAILURE); + break; + default: { + // parent + int status; + pid_t retVal; + + while (1) { + retVal = waitpid(pid, &status, 0); + if (retVal != -1) + break; + if (errno != EINTR) { + fprintf(debugContext->err, "Error: waitpid() " + "failed: %s\n", strerror(errno)); + break; + } + } + if (retVal == -1) + break; + + if (WIFEXITED(status)) { + fprintf(debugContext->err, "Exit status: %d\n", + WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + fprintf(debugContext->err, "Terminated on signal %d.\n", + WTERMSIG(status)); + } else { + fprintf(debugContext->err, "Error: weird exit status.\n"); + } + break; + } + } + } +#else + fprintf(debugContext->err, "Cannot execute: not supported on this " + "platform.\n"); +#endif + +err: + for (i = 1; i < argc - 1; i++) { + if (handles[i] != NULL) + uio_releaseStdioAccess(handles[i]); + } + uio_free(handles); + uio_free((void *) newArgs); + uio_closeDir(tempDir); + + return errCode; +} + +static int +debugCmdExit(DebugContext *debugContext, int argc, char *argv[]) { + debugContext->exit = true; + (void) argc; + (void) argv; + return 0; +} + +static int +debugCmdFwriteTest(DebugContext *debugContext, int argc, char *argv[]) { + uio_Stream *stream; + const char testString[] = "0123456789\n"; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + stream = uio_fopen(debugContext->cwd, argv[1], "w+b"); + if (stream == NULL) { + fprintf(debugContext->err, "Could not open file: %s\n", + strerror(errno)); + goto err; + } + + if (uio_fwrite(testString, strlen(testString), 1, stream) != 1) { + fprintf(debugContext->err, "uio_fwrite() failed: %s\n", + strerror(errno)); + goto err; + } + if (uio_fputs(testString, stream) == EOF) { + fprintf(debugContext->err, "uio_fputs() failed: %s\n", + strerror(errno)); + goto err; + } + if (uio_fseek(stream, 15, SEEK_SET) != 0) { + fprintf(debugContext->err, "uio_fseek() failed: %s\n", + strerror(errno)); + goto err; + } + if (uio_fputc('A', stream) != 'A') { + fprintf(debugContext->err, "uio_fputc() failed: %s\n", + strerror(errno)); + goto err; + } + if (uio_fseek(stream, 0, SEEK_SET) != 0) { + fprintf(debugContext->err, "uio_fseek() failed: %s\n", + strerror(errno)); + goto err; + } + { + char buf[6]; + char *ptr; + int i; + i = 1; + while (1) { + ptr = uio_fgets(buf, sizeof buf, stream); + if (ptr == NULL) + break; + fprintf(debugContext->out, "%d: [%s]\n", i, ptr); + i++; + } + if (uio_ferror(stream)) { + fprintf(debugContext->err, "uio_fgets() failed: %s\n", + strerror(errno)); + goto err; + } + uio_clearerr(stream); + } + if (uio_fseek(stream, 4, SEEK_END) != 0) { + fprintf(debugContext->err, "uio_fseek() failed: %s\n", + strerror(errno)); + goto err; + } + { + char buf[2000]; + memset(buf, 'Q', sizeof buf); + if (uio_fwrite(buf, 100, 20, stream) != 20) { + fprintf(debugContext->err, "uio_fwrite() failed: %s\n", + strerror(errno)); + goto err; + } + } + if (uio_fseek(stream, 5, SEEK_SET) != 0) { + fprintf(debugContext->err, "uio_fseek() failed: %s\n", + strerror(errno)); + goto err; + } + if (uio_fputc('B', stream) != 'B') { + fprintf(debugContext->err, "uio_fputc() failed: %s\n", + strerror(errno)); + goto err; + } + uio_fclose(stream); + return 0; +err: + uio_fclose(stream); + return 1; +} + +static int +listOneDir(DebugContext *debugContext, const char *arg) { + uio_DirList *dirList; + int i; + const char *pattern; + const char *cpath; + char *buf = NULL; + + if (arg[0] == '\0') { + cpath = arg; + pattern = "*"; + } else { + pattern = strrchr(arg, '/'); + if (pattern == NULL) { + // No directory component in 'arg'. + cpath = ""; + pattern = arg; + } else if (pattern[1] == '\0') { + // 'arg' ends on / + cpath = arg; + pattern = "*"; + } else { + if (pattern == arg) { + cpath = "/"; + } else { + buf = uio_malloc(pattern - arg + 1); + memcpy(buf, arg, pattern - arg); + buf[pattern - arg] = '\0'; + cpath = buf; + } + pattern++; + } + } +#ifdef HAVE_GLOB + dirList = uio_getDirList(debugContext->cwd, cpath, pattern, + match_MATCH_GLOB); +#else + if (pattern[0] == '*' && pattern[1] == '\0') { + dirList = uio_getDirList(debugContext->cwd, cpath, "", + match_MATCH_PREFIX); + } else { + dirList = uio_getDirList(debugContext->cwd, cpath, pattern, + match_MATCH_LITERAL); + } +#endif + if (dirList == NULL) { + fprintf(debugContext->out, "Error in uio_getDirList(): %s.\n", + strerror(errno)); + if (buf != NULL) + uio_free(buf); + return 1; + } + for (i = 0; i < dirList->numNames; i++) + fprintf(debugContext->out, "%s\n", dirList->names[i]); + uio_DirList_free(dirList); + if (buf != NULL) + uio_free(buf); + return 0; +} + +static int +debugCmdLs(DebugContext *debugContext, int argc, char *argv[]) { + int argI; + int retVal; + + if (argc == 1) + return listOneDir(debugContext, unconst("")); + + for (argI = 1; argI < argc; argI++) { + retVal = listOneDir(debugContext, argv[argI]); + if (retVal != 0) + return retVal; + } + + return 0; +} + +static int +debugCmdMem(DebugContext *debugContext, int argc, char *argv[]) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_printPointers(debugContext->out); +#else + fprintf(debugContext->out, "Memory debugging not compiled in.\n"); +#endif + (void) argc; + (void) argv; + return 0; +} + +static int +debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]) { + int retVal; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + retVal = uio_mkdir(debugContext->cwd, argv[1], 0777); + if (retVal == -1) { + fprintf(debugContext->err, "Could not create directory: %s\n", + strerror(errno)); + return 1; + } + return 0; +} + +static int +debugCmdMount(DebugContext *debugContext, int argc, char *argv[]) { + if (argc == 1) { + uio_printMounts(debugContext->out, repository); +// uio_printMountTree(debugContext->out, repository->mountTree, 0); + } + (void) argv; + return 0; +} + +static int +debugCmdMv(DebugContext *debugContext, int argc, char *argv[]) { + int retVal; + + if (argc != 3) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + retVal = uio_rename(debugContext->cwd, argv[1], + debugContext->cwd, argv[2]); + if (retVal == -1) { + fprintf(debugContext->err, "Could not rename: %s\n", strerror(errno)); + return 1; + } + return 0; +} + +static int +debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]) { + uio_DirHandle_print(debugContext->cwd, debugContext->out); + (void) argc; + (void) argv; + return 0; +} + +static int +debugCmdRm(DebugContext *debugContext, int argc, char *argv[]) { + int retVal; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + retVal = uio_unlink(debugContext->cwd, argv[1]); + if (retVal == -1) { + fprintf(debugContext->err, "Could not remove file: %s\n", + strerror(errno)); + return 1; + } + return 0; +} + +static int +debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]) { + int retVal; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + retVal = uio_rmdir(debugContext->cwd, argv[1]); + if (retVal == -1) { + fprintf(debugContext->err, "Could not remove directory: %s\n", + strerror(errno)); + return 1; + } + return 0; +} + +static int +debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) { + struct stat statBuf; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + if (uio_stat(debugContext->cwd, argv[1], &statBuf) == -1) { + // errno is set + int savedErrno; + savedErrno = errno; + fprintf(debugContext->err, "Could not stat file: %s\n", + strerror(errno)); + errno = savedErrno; + return 1; + } + + fprintf(debugContext->out, + "size %ld bytes\n" + "uid %d gid %d mode 0%o\n", + (unsigned long) statBuf.st_size, + (unsigned int) statBuf.st_uid, + (unsigned int) statBuf.st_gid, + (unsigned int) statBuf.st_mode & + (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID)); + // Can't do these next three in one fprintf, as ctime uses a static buffer + // that is overwritten with each call. + fprintf(debugContext->out, + "last access: %s", ctime(&statBuf.st_atime)); + fprintf(debugContext->out, + "last modification: %s", ctime(&statBuf.st_mtime)); + fprintf(debugContext->out, + "last status change: %s", ctime(&statBuf.st_ctime)); + + return 0; +} + +static int +debugCmdWriteTest(DebugContext *debugContext, int argc, char *argv[]) { + uio_Handle *handle; + const char testString[] = "Hello world!\n"; + + if (argc != 2) { + fprintf(debugContext->err, "Invalid number of arguments.\n"); + return 1; + } + + handle = uio_open(debugContext->cwd, argv[1], + O_WRONLY | O_CREAT | O_EXCL +#ifdef WIN32 + | O_BINARY +#endif + , 0644); + if (handle == NULL) { + fprintf(debugContext->err, "Could not open file: %s\n", + strerror(errno)); + return 1; + } + + if (uio_write(handle, testString, sizeof testString) == -1) { + fprintf(debugContext->err, "Write failed: %s\n", + strerror(errno)); + return 1; + } + + uio_close(handle); + return 0; +} + + diff --git a/src/libs/uio/debug.h b/src/libs/uio/debug.h new file mode 100644 index 0000000..fd6a3ff --- /dev/null +++ b/src/libs/uio/debug.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 UIO_DEBUG_H +#define UIO_DEBUG_H + +void uio_debugInteractive(FILE *in, FILE *out, FILE *err); + + +#endif /* _DEBUG_H */ + + diff --git a/src/libs/uio/defaultfs.c b/src/libs/uio/defaultfs.c new file mode 100644 index 0000000..bf6d247 --- /dev/null +++ b/src/libs/uio/defaultfs.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 "defaultfs.h" +#include "uioport.h" + +extern uio_FileSystemHandler stdio_fileSystemHandler; +#ifdef HAVE_ZIP +extern uio_FileSystemHandler zip_fileSystemHandler; +#endif + +const uio_DefaultFileSystemSetup defaultFileSystems[] = { + { uio_FSTYPE_STDIO, "stdio", &stdio_fileSystemHandler }, +#ifdef HAVE_ZIP + { uio_FSTYPE_ZIP, "zip", &zip_fileSystemHandler }, +#endif +}; + +int +uio_numDefaultFileSystems(void) { + return sizeof defaultFileSystems / sizeof defaultFileSystems[0]; +} + + diff --git a/src/libs/uio/defaultfs.h b/src/libs/uio/defaultfs.h new file mode 100644 index 0000000..711ac0c --- /dev/null +++ b/src/libs/uio/defaultfs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 uio_DEFAULTFS_H +#define uio_DEFAULTFS_H + +typedef struct uio_DefaultFileSystemSetup uio_DefaultFileSystemSetup; + +#include "iointrn.h" +#include "uioport.h" +#include "fstypes.h" + +struct uio_DefaultFileSystemSetup { + uio_FileSystemID id; + const char *name; + uio_FileSystemHandler *handler; +}; + +extern const uio_DefaultFileSystemSetup defaultFileSystems[]; + +int uio_numDefaultFileSystems(void); + +#endif /* uio_DEFAULTFS_H */ + diff --git a/src/libs/uio/doc/basics b/src/libs/uio/doc/basics new file mode 100644 index 0000000..e04afd8 --- /dev/null +++ b/src/libs/uio/doc/basics @@ -0,0 +1,178 @@ +-= Introduction =- + +The file io system I present here provides the user with a virtual file +systems on which normal POSIX-like file operations can be performed. + +A virtual file system consists of a number of underlying filesystems merged +together in one directory structure. +Underlying file systems can be POSIX filesystems, or zip archives, but +others can be added easilly. +Filesystems are grafted into the virtual filesystem either explicitely, by +mounting, or implicitely, by having files of a specific type (for instance +those ending on .zip) mounted automatically. +When a filesystem is mounted to a directory which already exists in an +earlier mounted file system, files in the later mounted file system hide +files in an earlier mounted file system. Files present in a filesystem +mounted earlier that don't exist in a filesystem mounted later will remain +visible. +Accessing compressed files inside compressed files is possible, though slow. + + +-= Nomenclature =- +'repository' +A collection of files from various sources as a virtual file system. + +'physical directory structure' +An underlying filesystem, such as a POSIX filesystem, or a .zip archive. + +'logical directory structure' +The merger of one or more Physical directory structures. + +'mounting' +Grafting a physical directory structure in a logical directory system. + +When mounting several dirs on top of eachother, I refer to later mounted +dirs as 'higher' dirs, and earlier mounted dirs as 'lower' dirs. + +'directory entry' +A file or subdirectory within a directory. + + +-= API =- + +Types: +uio_Repository - A struct describing a repository +uio_Handle - A handle for working on files +uio_DirHandle - A handle for working on directories + + +TODO: functions + + +-= Behaviour relating directory structures =- + +The design of the virtual filesystem is guided by the following rules: +- Combined directories contain the entries of each directory. + If some of the source directories contain an entry with the same name, + the combined directory will contain the entry of the topmost directory. + (this means a directory can hide a file and the other way around) +- Entries hidden in this way are never accessable. +- Where possible, actions on directory entries within a combined directory + are done as in a normal file system. +Because of these, some design decisions have been made: +- New entries are created in the topmost writable filesystem. If the path to + the directory where the entry is to be created does not exist, it is + created [1]. +- When a file is to be opened for writing, and the file in the combined + filesystem exists, but is not writable, and there exists a writable + filesystem, mounted higher than the filesystem where the existing file + resides, the file is first copied to the topmost writable filesystem. + +[1] I could have decided to use a pre-existing writable directory if one + is available. + With this choice, when no such pre-existing directory exists, + it would make sense to complete the path in the writable filesystem + in which the part of the path that does exist is the longest. + As you can't create a directory inside a specific filesystem, + this would complicate and confuse things for the user. + + +In specific, for various actions: + +opening a file: +- if O_RDONLY: + - open the file in the highest dir. +- if O_WRONLY or O_RDWR: + - if file already exists: + - if O_CREAT and O_EXCL: return EEXIST + - if file is writable, use that one + - if file is not writable, copy it to the highest writable location + higher than the location of that file (don't bother if O_TRUNC) and use + the new file. If necessary, create the path leading upto it. + If no such location exists, return EACCESS. + - if file does not exist: + - if not O_CREAT: return ENOENT + - if the path to the file does not exists, return ENOENT. + - find the highest writable dir and open the file there, creating + the path leading upto it if necessary. + +removing a file: +- try removing the specified file from all physical directory structures + for a repository. +- once a file is encountered that can't be removed, return an error for + that and don't try the rest. + +creating a directory: +- as for opening a file with O_WRONLY | O_CREAT | O_EXCL + +removing a directory: +- as for removing a file + (but a physical directory not being empty is a reason for failure) + + +-= Limitations =- + +There's no limit to the length of a path in the logical file system. +Paths in the underlying physical filesystem can be limited though. + +At the moment, the system is not thread safe. Only one thread should access +a repository at once. Seperate threads working on seperate repositories is +no problem, as long as the repositories don't overlap. + + +-= Internals =- + +Types: + +uio_MountTree + A node in a data structure describing the mounted directories. + +uio_PRoot + A struct describing the a physical file system. + +uio_PRootExtra + Filesystem-dependant extra data for a PRoot. + +uio_GPRoot + Generic filesystem-dependant data for a PRoot, used as uio_PRootExtra. + +uio_GPRootExtra + Extra filesystem-dependant data for a PRoot, when using uio_GPRoot for + generic filesystem-dependant data. + +uio_GPDir + Generic structure representing a node in a physical directory structure + describing one directory. + +uio_GPFile + Generic structure describing a file in a physical file system. + + +Helper functions (defined in ioaux.c): + +uio_copyFilePhysical + Copy a file from one physical directory to another. + +uio_getPathPhysicalDirs + Get handles to the (existing) physical dirs that are effective in a + path 'path' relative from 'dirHandle' + +uio_getPhysicalAccess + Find PDirHandle and MountInfo structures for reading and writing for a path + from a DirHandle. + +uio_makePath + Create a directory inside a physical directory. All non-existant + parent directories will be created as well. + +uio_resolvePath + Determine the absolute path given a path relative to a given directory. + +uio_verifyPath + Test whether a path is valid and exists. + +uio_walkPhysicalPath + Follow a path starting from a specified physical dir for as long as + possible. + + diff --git a/src/libs/uio/doc/conventions b/src/libs/uio/doc/conventions new file mode 100644 index 0000000..f156101 --- /dev/null +++ b/src/libs/uio/doc/conventions @@ -0,0 +1,30 @@ +This file describes the various conventions used in the source. + + +-= Naming of constructors and destructors =- + +uio_Thing *uio_Thing_new(args) + Allocates the structure, and initialises it from the arguments. + Arguments are always used as-is; if they are reference-counted, + and the caller still needs a reference, it is up to the caller + to increment the reference counter. +void uio_Thing_delete(uio_Thing *thing) + Frees the structure and all sub-structures. + Decrements reference counters to shared structures. +uio_Thing *uio_Thing_alloc() + Allocates memory for a new structure. + Could be omited for standard allocators. + Particularly relevant when a non-standard allocation scheme is + used (for instance, allocation from a pool of pre-allocated strucures). +void uio_Thing_free(Thing *thing) + Frees the memory allocated for thing (reverse of uio_Thing_alloc). + Could be omited for standard deallocators. +void uio_Thing_ref(Thing *thing) + Increments the reference counter to the structure. +void uio_Thing_unref(Thing *thing) + Decrements the reference counter to the structure. + Calls uio_Thing_Delete() if the refcounter becomes 0. + +These functions are always declared and defined in this order. + + diff --git a/src/libs/uio/doc/todo b/src/libs/uio/doc/todo new file mode 100644 index 0000000..b9f232c --- /dev/null +++ b/src/libs/uio/doc/todo @@ -0,0 +1,144 @@ +Needed for use in UQM: +- documentation +- configuring for GLOB +- when doing uio_getStdioPhysical(), if write access is required, but + not available on the original location, also copy the file to the + temporary dir. +- Call fsync() at appropriate times. + +Documentation: +- use doxygen +- Warning: getting (part of) the contents of a directory is fairly slow, + as dirs need to be merged. +- It would be (theoretically) possible to add HTTP and FTP support for + remote file systems. +- on adding extra file system types: + open, mkdir, rmdir, and unlink should themselves make sure that + the physical structure is kept up to date when an entry is + removed or added. +- stream stuff is not thread safe +- uio_fflush() does not accept NULL as argument to flush all streams, + like stdio fflush() does. +- uio_close() does never fail +- The physical open function should call uio_Handle_new and store its own + data in it. + The physical close function should delete its own data. It's called + Will cleanup the general Handle. + Analogous for mount/unmount with PRoot +- No need to store MountHandles. They will be automatically freed + when the repository is closed. +- You can use mount stuff from other repositories. +- ".." works by nullifying one path component, not by following the ".." link + in the directory. "/a/../b" will always be functionally equivalent to + "/b", even when "/a" is a symlink. + +Testing: +- Test mounting an UNC directory + +Bugs: +- 'openDir(repository, "dir/")' will have the trailing '/' + in the dirHandle, which will cause problems. +- 'openDirRelative(repository, "/")' causes segfaults later on +- uio_rename() doesn't work on directories +- uio_getPhysicalAccess() needs to be changed so that it works the same on + dirs as on files. stat() can be cleaned up too then. +- uio_getPhysicalAccess() will not return ENOENT when (only) the last + component does not exist, even when O_RDONLY is used. + For O_RDRW, O_CREAT should probably be checked too (function description + needs to be updated too then). +- A lot of inlining is not possible because of the order of function + definitions. +- sizeof(size_t) may be less than 4 on some platforms. Check what problems + this may cause. At least the size of zip_INPUT_BUFFER_SIZE is an + issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime. +- remove() is probably more compatible than unlink(). remove() is part + of the C standard (as well as POSIX), unlink() is just POSIX. +- seeking in files opened as "text" on Windows goes wrong. +- Network paths on Windows are not accepted. +- No CRLF translation (and ^Z recognition) is done for files read from + zip files, even though that may be expected on Windows. +- The order in which "uio_unmountAllDirs() unmounts the directories, may lead + it to unmount a dir while there still is a file descriptor for another + dir open, causing a warning. + +Extra features (not necessary for UQM): +- Make functions to use for uio_malloc, uio_free and uio_realloc + configurable at init. +- add uio_mmap() +- add match_MATCH_ALL and match_MATCH_NONE +- automounting + - Unmount automounted filesystems when the originating filesystem + is unmounted. +- when doing stat() on a directory, merge the stat info of the underlying + physical dirs. +- read directory information from zip files. +- implement ungetc() +- make fileblocks public +- setmode() for windows? +- prevent aliasing of files/dirs, both between two physical file + systems (where possible), as within the same filesystem (in particular + stdio). On Windows, aliasing can occur easilly when a file or dir + is accessed with the long vs the short ("PROGRA~1") name, or when + capitalisation is involved. +- accept non-dir, non-regular-file dir entries in stdio. +- Add non-merging mounts. Make 'merging' an option for the mount. +- make uio_rename() work cross-fs, or provide a wrapper which does that. + (the system rename() doesn't work cross-fs either, so keeping uio_rename() + as it is makes sense, as I'm trying to stay close to the system functions, + even though hiding file systems from the user would be nicer) +- Add a readdir_r(). Right now, for Symbian readdir_r() is defined in + uioport.h, but the actual implementation is out of the uio tree. + +Optimisations (not necessary for UQM): +- use mmap for fileBlocks +- Use a pre-allocated pool of hash table entries for allocHashEntry. +- optimise certain strings (specifically directory entries) by making + a string type which has pointers to a shared char array. + Invariant: if two strings are the same, they point to the same + character array. Consequence: string comparison is pointer comparison. + Making a new string would imply checking the existing strings. + Existing strings should be in a hash table. +- optimise paths + Encode all paths internally as (double-linked) chains of path components + This way, operations like going up one path component, are cheap. + Each path component could either be represented by a pointer to a string, + (possibly shared as above), or as a pair of begin and end pointers + (or begin pointer and length). In the latter case, there's no need for + copying strings when parsing a user-supplied string, but sharing of strings + would not be possible. + It's probably possible to use both methods next to eachother; + shared strings for stored paths, and pointers into a path for user-supplied + strings (which would be freed when the call returns). + Instead of a linked list, perhaps an array can be used, as paths rarely + change. It would be a problem if for some reason, recursively path + components are added to a path. +- (maybe) keep track of already issued handles, so that the ref counter + could just be incremented, instead of issuing a new one. + (uio_getPDirEntryHandle) +- Make it possible for people to add their own file system, without giving + away the internals. (no wanton including of .h files) +- Don't cache stdio dir structure + (and don't read the dir leading up to the dir that is actually needed) +- mounting foo to /Foo and foo/bar to /Foo/Bar should result in only one + physical structure. Preferably, it shouldn't even lead to two mountInfo + structures for /Foo/Bar, so that merging the dirs is not unnecessarilly + expensive. +- add uio_access() + (uqm fileExists can be redone then) +- implement the physical function cleanup() to clean up the physical + structure. Int argument that specifies how thoroughly. + On it depends whether cache is cleaned, whether stuff is realloc'ed + to defragment memory, etc. + +Cleanups (not necessary for UQM): +- rename function names to be more like class names for as far as that's + not already done. + uio_PRoot_unref etc +- Add uio_fatal(), uio_error(), uio_warning() +- Clean up the include structure +- use stdint.h and stdbool.h types directly, instead of using uio_int16 etc. + Remove types.h, and instead, if these types are missing on some platforms, + put the fixes in port.h. + + + diff --git a/src/libs/uio/fileblock.c b/src/libs/uio/fileblock.c new file mode 100644 index 0000000..bb4f466 --- /dev/null +++ b/src/libs/uio/fileblock.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +#define uio_INTERNAL_FILEBLOCK + +#include "iointrn.h" +#include "fileblock.h" +#include "uioport.h" + +#include <errno.h> + +static uio_FileBlock *uio_FileBlock_new(uio_Handle *handle, int flags, + off_t offset, size_t blockSize, char *buffer, size_t bufSize, + off_t bufOffset, size_t bufFill, size_t readAheadBufSize); +static inline uio_FileBlock *uio_FileBlock_alloc(void); +static void uio_FileBlock_free(uio_FileBlock *block); + +// caller should uio_Handle_ref(handle) (unless it doesn't need it's own +// reference anymore). +static uio_FileBlock * +uio_FileBlock_new(uio_Handle *handle, int flags, off_t offset, + size_t blockSize, char *buffer, size_t bufSize, off_t bufOffset, + size_t bufFill, size_t readAheadBufSize) { + uio_FileBlock *result; + + result = uio_FileBlock_alloc(); + result->handle = handle; + result->flags = flags; + result->offset = offset; + result->blockSize = blockSize; + result->buffer = buffer; + result->bufSize = bufSize; + result->bufOffset = bufOffset; + result->bufFill = bufFill; + result->readAheadBufSize = readAheadBufSize; + return result; +} + +static inline uio_FileBlock * +uio_FileBlock_alloc(void) { + return uio_malloc(sizeof (uio_FileBlock)); +} + +static inline void +uio_FileBlock_free(uio_FileBlock *block) { + uio_free(block); +} + +uio_FileBlock * +uio_openFileBlock(uio_Handle *handle) { + // TODO: if mmap support is available, and it is available natively + // for the filesystem (make some sort of sysctl for filesystems + // to check this?), use mmap. + // mmap the entire file if it's small enough. + // N.B. Keep in mind streams of which the size is not known in + // advance. + struct stat statBuf; + if (uio_fstat(handle, &statBuf) == -1) { + // errno is set + return NULL; + } + uio_Handle_ref(handle); + return uio_FileBlock_new(handle, 0, 0, statBuf.st_size, NULL, 0, 0, 0, 0); +} + +uio_FileBlock * +uio_openFileBlock2(uio_Handle *handle, off_t offset, size_t size) { + // TODO: mmap (see uio_openFileBlock) + + // TODO: check if offset and size are acceptable. + // Need to handle streams of which the size is unknown. +#if 0 + if (uio_stat(handle, &statBuf) == -1) { + // errno is set + return NULL; + } + if (statBuf.st_size > offset || (statBuf.st_size - offset > size)) { + // NOT: 'if (statBuf.st_size > offset + size)', to protect + // against overflow. + + } +#endif + uio_Handle_ref(handle); + return uio_FileBlock_new(handle, 0, offset, size, NULL, 0, 0, 0, 0); +} + +static inline ssize_t +uio_accessFileBlockMmap(uio_FileBlock *block, off_t offset, size_t length, + char **buffer) { + // TODO + errno = ENOSYS; + (void) block; + (void) offset; + (void) length; + (void) buffer; + return -1; +} + +static inline ssize_t +uio_accessFileBlockNoMmap(uio_FileBlock *block, off_t offset, size_t length, + char **buffer) { + ssize_t numRead; + off_t start; + off_t end; + size_t bufSize; + char *oldBuffer; + //size_t oldBufSize; + + // Don't go beyond the end of the block. + if (offset > (off_t) block->blockSize) { + *buffer = block->buffer; + return 0; + } + if (length > block->blockSize - offset) + length = block->blockSize - offset; + + if (block->buffer != NULL) { + // Check whether the requested data is already in the buffer. + if (offset >= block->bufOffset && + (offset - block->bufOffset) + length < block->bufFill) { + *buffer = block->buffer + (offset - block->bufOffset); + return length; + } + } + + if (length < block->readAheadBufSize && + (block->flags & uio_FB_USAGE_MASK) != 0) { + // We can buffer more data. + switch (block->flags & uio_FB_USAGE_MASK) { + case uio_FB_USAGE_FORWARD: + // Read extra data after the requested data. + start = offset; + end = (block->readAheadBufSize > block->blockSize - offset) ? + block->blockSize : offset + block->readAheadBufSize; + break; + case uio_FB_USAGE_BACKWARD: + // Read extra data before the requested data. + end = offset + length; + start = (end <= (off_t) block->blockSize) ? + 0 : end - block->bufSize; + break; + case uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD: { + // Read extra data both before and after the requested data. + off_t extraBefore = (block->readAheadBufSize - length) / 2; + start = (offset < extraBefore) ? 0 : offset - extraBefore; + + end = (block->readAheadBufSize > block->blockSize - start) ? + block->blockSize : start + block->readAheadBufSize; + break; + } + } + } else { + start = offset; + end = offset + length; + } + bufSize = (length > block->readAheadBufSize) ? + length : block->readAheadBufSize; + + // Start contains the start index in the block of the data we're going + // to read. + // End contains the end index. + // bufSize contains the size of the buffer. bufSize >= end - start. + + oldBuffer = block->buffer; + //oldBufSize = block->bufSize; + if (block->buffer != NULL || block->bufSize != bufSize) { + // We don't have a buffer, or we have one, but of the wrong size. + block->buffer = uio_malloc(bufSize); + block->bufSize = bufSize; + } + + if (oldBuffer != NULL) { + // TODO: If we have part of the data still in the old buffer, we + // can keep that. + // memmove(...) + + if (oldBuffer != block->buffer) + uio_free(oldBuffer); + } + block->bufFill = 0; + block->bufOffset = start; + + // TODO: lock handle + if (uio_lseek(block->handle, block->offset + start, SEEK_SET) == + (off_t) -1) { + // errno is set + return -1; + } + + numRead = uio_read(block->handle, block->buffer, end - start); + if (numRead == -1) { + // errno is set + // TODO: unlock handle + return -1; + } + // TODO: unlock handle + + block->bufFill = numRead; + *buffer = block->buffer + (offset - block->bufOffset); + if (numRead <= (offset - block->bufOffset)) + return 0; + if ((size_t) numRead >= length) + return length; + return numRead - offset; +} + +// block remains usable until the next call to uio_accessFileBlock +// with the same block as argument, or until uio_closeFileBlock with +// that block as argument. +// The 'offset' parameter is wrt. the start of the block. +// Requesting access to data beyond the file block is not an error. The +// FileBlock is meant to be used as a replacement of seek() and read(), and +// as with those functions, trying to go beyond the end of a file just +// goes to the end. The return value is the number of bytes in the buffer. +ssize_t +uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length, + char **buffer) { + if (block->flags & uio_FB_USE_MMAP) { + return uio_accessFileBlockMmap(block, offset, length, buffer); + } else { + return uio_accessFileBlockNoMmap(block, offset, length, buffer); + } +} + +int +uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer, + size_t length) { + if (block->flags & uio_FB_USE_MMAP) { + // TODO + errno = ENOSYS; + return -1; + } else { + ssize_t numCopied = 0; + ssize_t readResult; + + // Don't go beyond the end of the block. + if (offset > (off_t) block->blockSize) + return 0; + if (length > block->blockSize - offset) + length = block->blockSize - offset; + + // Check whether (part of) the requested data is already in our + // own buffer. + if (block->buffer != NULL && offset >= block->bufOffset + && offset < block->bufOffset + (off_t) block->bufFill) { + size_t toCopy = block->bufFill - offset; + if (toCopy > length) + toCopy = length; + memcpy(buffer, block->buffer + (offset - block->bufOffset), + toCopy); + numCopied += toCopy; + length -= toCopy; + if (length == 0) + return numCopied; + buffer += toCopy; + offset += toCopy; + } + + // TODO: lock handle + if (uio_lseek(block->handle, block->offset + offset, SEEK_SET) == + (off_t) -1) { + // errno is set + return -1; + } + + readResult = uio_read(block->handle, buffer, length); + // TODO: unlock handle + if (readResult == -1) { + // errno is set + return -1; + } + numCopied += readResult; + + return numCopied; + } +} + +int +uio_closeFileBlock(uio_FileBlock *block) { + if (block->flags & uio_FB_USE_MMAP) { +#if 0 + if (block->buffer != NULL) + uio_mmunmap(block->buffer); +#endif + } else { + if (block->buffer != NULL) + uio_free(block->buffer); + } + uio_Handle_unref(block->handle); + uio_FileBlock_free(block); + return 0; +} + +// Usage is the or'ed value of zero or more of uio_FB_USAGE_FORWARD, +// and uio_FB_USAGE_BACKWARD. +void +uio_setFileBlockUsageHint(uio_FileBlock *block, int usage, + size_t readAheadBufSize) { + block->flags = (block->flags & ~uio_FB_USAGE_MASK) | + (usage & uio_FB_USAGE_MASK); + block->readAheadBufSize = readAheadBufSize; +} + +// Call if you want the memory used by the fileblock to be released, but +// still want to use the fileblock later. If you don't need the fileblock, +// call uio_closeFileBlock() instead. +void +uio_clearFileBlockBuffers(uio_FileBlock *block) { + if (block->buffer != NULL) { + uio_free(block->buffer); + block->buffer = NULL; + } +} + + diff --git a/src/libs/uio/fileblock.h b/src/libs/uio/fileblock.h new file mode 100644 index 0000000..97a81e1 --- /dev/null +++ b/src/libs/uio/fileblock.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_FILEBLOCK_H_ +#define LIBS_UIO_FILEBLOCK_H_ + +typedef struct uio_FileBlock uio_FileBlock; + +#include "io.h" +#include "uioport.h" + +#include <sys/types.h> + +#define uio_FB_USAGE_FORWARD 1 +#define uio_FB_USAGE_BACKWARD 2 +#define uio_FB_USAGE_MASK (uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD) + +#ifdef uio_INTERNAL_FILEBLOCK + +// A fileblock represents a contiguous block of data from a file. +// It's purpose is to avoid needless copying of data, while enabling +// buffering. + +struct uio_FileBlock { + uio_Handle *handle; + int flags; + // See above for uio_FB_USAGE_FORWARD, uio_FB_USAGE_BACKWARD. +#define uio_FB_USE_MMAP 4 + off_t offset; + // Offset to the start of the block in the file. + size_t blockSize; + // Size of the block of data represented by this FileBlock. + char *buffer; + // either allocated buffer, or buffer to mmap'ed area. + size_t bufSize; + // Size of the buffer. + off_t bufOffset; + // Offset of the start of the buffer into the block. + size_t bufFill; + // Part of 'buffer' which is in use. + size_t readAheadBufSize; + // Try to read up to this many bytes at a time, even when less + // is immediately needed. +}; +// INV: The FileBlock represents 'fileData[offset..(offset + blockSize - 1)]' +// where 'fileData' is the contents of the file. +// INV: If buf != NULL then: +// bufFill <= bufSize +// bufFill <= blockSize +// buffer[0..bufFill - 1] == fileData[ +// (offset + bufOffset)..(offset + bufOffset + bufFill - 1)] + + +#endif /* uio_INTERNAL_FILEBLOCK */ + +uio_FileBlock *uio_openFileBlock(uio_Handle *handle); +uio_FileBlock *uio_openFileBlock2(uio_Handle *handle, off_t offset, + size_t size); +ssize_t uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length, + char **buffer); +int uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer, + size_t length); +int uio_closeFileBlock(uio_FileBlock *block); +#define uio_FB_READAHEAD_BUFSIZE_MAX ((size_t) -1) +void uio_setFileBlockUsageHint(uio_FileBlock *block, int usage, + size_t readAheadBufSize); +void uio_clearFileBlockBuffers(uio_FileBlock *block); + +#endif /* LIBS_UIO_FILEBLOCK_H_ */ + + diff --git a/src/libs/uio/fstypes.c b/src/libs/uio/fstypes.c new file mode 100644 index 0000000..d940e8f --- /dev/null +++ b/src/libs/uio/fstypes.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <errno.h> +#include <stdio.h> + +#include "iointrn.h" +#include "uioport.h" +#include "fstypes.h" +#include "mem.h" +#include "defaultfs.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + + +static uio_bool uio_validFileSystemHandler(uio_FileSystemHandler *handler); +static uio_FileSystemInfo *uio_FileSystemInfo_new(uio_FileSystemID id, + uio_FileSystemHandler *handler, char *name); +static uio_FileSystemInfo **uio_getFileSystemInfoPtr(uio_FileSystemID id); + +static inline uio_FileSystemInfo *uio_FileSystemInfo_alloc(void); + +static inline void uio_FileSystemInfo_free( + uio_FileSystemInfo *fileSystemInfo); + + +uio_FileSystemInfo *uio_fileSystems = NULL; + // list sorted by id + + + +void +uio_registerDefaultFileSystems(void) { + int i; + int num; + uio_FileSystemID registerResult; + + num = uio_numDefaultFileSystems(); + for (i = 0; i < num; i++) { + registerResult = uio_registerFileSystem( + defaultFileSystems[i].id, + defaultFileSystems[i].name, + defaultFileSystems[i].handler); + switch (registerResult) { + case 0: + fprintf(stderr, "Warning: Default file system '%s' is " + "already registered.\n", + defaultFileSystems[i].name); + break; + case -1: + fprintf(stderr, "Error: Could not register '%s' file \n" + "system: %s\n", defaultFileSystems[i].name, + strerror(errno)); + break; + default: + assert(registerResult == defaultFileSystems[i].id); + break; + } + } +} + +void +uio_unRegisterDefaultFileSystems(void) { + int i; + int num; + + num = uio_numDefaultFileSystems(); + for (i = 0; i < num; i++) { + if (uio_unRegisterFileSystem(defaultFileSystems[i].id) == -1) { + fprintf(stderr, "Could not unregister '%s' file system: %s\n", + defaultFileSystems[i].name, strerror(errno)); + } + } +} + +// if wantedID = 0, just pick one +// if wantedID != 0, 0 will be returned if that id wasn't available +// a copy of 'name' is made +uio_FileSystemID +uio_registerFileSystem(uio_FileSystemID wantedID, const char *name, + uio_FileSystemHandler *handler) { + uio_FileSystemInfo **ptr; + + if (!uio_validFileSystemHandler(handler)) { + errno = EINVAL; + return -1; + } + if (wantedID == 0) { + // Search for the first free id >= uio_FIRST_CUSTOM_ID + // it is put in wantedID + + for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) + if ((*ptr)->id >= uio_FS_FIRST_CUSTOM_ID) + break; + + wantedID = uio_FS_FIRST_CUSTOM_ID; + while (*ptr != NULL) { + if ((*ptr)->id != wantedID) { + // wantedID is not in use + break; + } + wantedID++; + ptr = &(*ptr)->next; + } + // wantedID contains the new ID + } else { + // search for the place in the list where to insert the wanted + // id, keeping the list sorted + for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) { + if ((*ptr)->id <= wantedID) { + if ((*ptr)->id == wantedID) + return 0; + break; + } + } + + } + // ptr points to the place where the new link can inserted + + if (handler->init != NULL && handler->init() == -1) { + // errno is set + return -1; + } + + { + uio_FileSystemInfo *newInfo; + + newInfo = uio_FileSystemInfo_new(wantedID, handler, uio_strdup(name)); + newInfo->next = *ptr; + *ptr = newInfo; + return wantedID; + } +} + +int +uio_unRegisterFileSystem(uio_FileSystemID id) { + uio_FileSystemInfo **ptr; + uio_FileSystemInfo *temp; + + ptr = uio_getFileSystemInfoPtr(id); + if (ptr == NULL) { + errno = EINVAL; + return -1; + } + if ((*ptr)->ref > 1) { + errno = EBUSY; + return -1; + } + + if ((*ptr)->handler->unInit != NULL && + ((*ptr)->handler->unInit() == -1)) { + // errno is set + return -1; + } + + temp = *ptr; + *ptr = (*ptr)->next; + +// uio_FileSystemHandler_unref(temp->handler); + uio_free(temp->name); + uio_FileSystemInfo_free(temp); + + return 0; +} + +static uio_bool +uio_validFileSystemHandler(uio_FileSystemHandler *handler) { + // Check for the essentials + if (handler->mount == NULL || + handler->umount == NULL || + handler->open == NULL || + handler->close == NULL || + handler->read == NULL || + handler->openEntries == NULL || + handler->readEntries == NULL || + handler->closeEntries == NULL) { +#ifdef DEBUG + fprintf(stderr, "Invalid file system handler.\n"); +#endif + return false; + } + return true; +} + +uio_FileSystemHandler * +uio_getFileSystemHandler(uio_FileSystemID id) { + uio_FileSystemInfo *ptr; + + for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) { + if (ptr->id == id) + return ptr->handler; + } + return NULL; +} + +uio_FileSystemInfo * +uio_getFileSystemInfo(uio_FileSystemID id) { + uio_FileSystemInfo *ptr; + + for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) { + if (ptr->id == id) + return ptr; + } + return NULL; +} + +static uio_FileSystemInfo ** +uio_getFileSystemInfoPtr(uio_FileSystemID id) { + uio_FileSystemInfo **ptr; + + for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) { + if ((*ptr)->id == id) + return ptr; + } + return NULL; +} + +// sets ref to 1 +static uio_FileSystemInfo * +uio_FileSystemInfo_new(uio_FileSystemID id, uio_FileSystemHandler *handler, + char *name) { + uio_FileSystemInfo *result; + + result = uio_FileSystemInfo_alloc(); + result->id = id; + result->handler = handler; + result->name = name; + result->ref = 1; + return result; +} + +// *** Allocators *** + +static inline uio_FileSystemInfo * +uio_FileSystemInfo_alloc(void) { + uio_FileSystemInfo *result = uio_malloc(sizeof (uio_FileSystemInfo)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_FileSystemInfo, (void *) result); +#endif + return result; +} + + +// *** Deallocators *** + +static inline void +uio_FileSystemInfo_free(uio_FileSystemInfo *fileSystemInfo) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_FileSystemInfo, (void *) fileSystemInfo); +#endif + uio_free(fileSystemInfo); +} + + diff --git a/src/libs/uio/fstypes.h b/src/libs/uio/fstypes.h new file mode 100644 index 0000000..3ff01a2 --- /dev/null +++ b/src/libs/uio/fstypes.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_FSTYPES_H_ +#define LIBS_UIO_FSTYPES_H_ + +typedef int uio_FileSystemID; +#define uio_FSTYPE_STDIO 1 +#define uio_FSTYPE_ZIP 2 + + +#ifdef uio_INTERNAL + +#define uio_FS_FIRST_CUSTOM_ID 0x10 + +#ifndef uio_INTERNAL_PHYSICAL +typedef void *uio_NativeHandle; +#endif + +// 'forward' declarations +typedef struct uio_FileSystemHandler uio_FileSystemHandler; +typedef struct uio_FileSystemInfo uio_FileSystemInfo; + +#include <sys/stat.h> +#include <sys/types.h> +#include "physical.h" +#include "uioport.h" + + +/* Structure describing how to access files _in_ a directory of a certain + * type (not files _of_ a certain type.) Except for mount(). + * in open(), the first arg points to the dir where the file should be + * in, and the second arg is the name of that file (no path) + */ +struct uio_FileSystemHandler { + int (*init) (void); + int (*unInit) (void); + void (*cleanup) (uio_PRoot *, int); + // Called to cleanup memory. The second argument specifies + // how thoroughly. + + struct uio_PRoot * (*mount) (uio_Handle *, int); + int (*umount) (uio_PRoot *); + + int (*access) (uio_PDirHandle *, const char *, int mode); + void (*close) (uio_Handle *); + // called when the last reference is closed, not + // necessarilly each time when uio_close() is called + int (*fstat) (uio_Handle *, struct stat *); + int (*stat) (uio_PDirHandle *, const char *, + struct stat *); + uio_PDirHandle * (*mkdir) (uio_PDirHandle *, const char *, mode_t); + uio_Handle * (*open) (uio_PDirHandle *, const char *, int, + mode_t); + ssize_t (*read) (uio_Handle *, void *, size_t); + int (*rename) (uio_PDirHandle *, const char *, + uio_PDirHandle *, const char *); + int (*rmdir) (uio_PDirHandle *, const char *); + off_t (*seek) (uio_Handle *, off_t, int); + ssize_t (*write) (uio_Handle *, const void *, size_t); + int (*unlink) (uio_PDirHandle *, const char *); + + uio_NativeEntriesContext (*openEntries) (uio_PDirHandle *); + int (*readEntries) (uio_NativeEntriesContext *, char *, + size_t); + void (*closeEntries) (uio_NativeEntriesContext); + + uio_PDirEntryHandle * (*getPDirEntryHandle) ( + const uio_PDirHandle *, const char *); + void (*deletePRootExtra) (uio_PRootExtra pRootExtra); + void (*deletePDirHandleExtra) ( + uio_PDirHandleExtra pDirHandleExtra); + void (*deletePFileHandleExtra) ( + uio_PFileHandleExtra pFileHandleExtra); +}; + +struct uio_FileSystemInfo { + int ref; + uio_FileSystemID id; + char *name; // name of the file system + uio_FileSystemHandler *handler; + struct uio_FileSystemInfo *next; +}; + +void uio_registerDefaultFileSystems(void); +void uio_unRegisterDefaultFileSystems(void); +uio_FileSystemID uio_registerFileSystem(uio_FileSystemID wantedID, + const char *name, uio_FileSystemHandler *handler); +int uio_unRegisterFileSystem(uio_FileSystemID id); +uio_FileSystemHandler *uio_getFileSystemHandler(uio_FileSystemID id); +uio_FileSystemInfo *uio_getFileSystemInfo(uio_FileSystemID id); + +#endif /* uio_INTERNAL */ + +#endif /* LIBS_UIO_FSTYPES_H_ */ + diff --git a/src/libs/uio/getint.h b/src/libs/uio/getint.h new file mode 100644 index 0000000..ad6e810 --- /dev/null +++ b/src/libs/uio/getint.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2007 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_GETINT_H_ +#define LIBS_UIO_GETINT_H_ + +/* All these functions return true on success, or false on failure */ + +#include "types.h" +#include "uioport.h" + +static inline uio_bool +uio_getU8(uio_Stream *stream, uio_uint8 *result) { + int val = uio_getc(stream); + if (val == EOF) + return false; + + *result = (uio_uint8) val; + return true; +} + +static inline uio_bool +uio_getS8(uio_Stream *stream, uio_sint8 *result) { + int val = uio_getc(stream); + if (val == EOF) + return false; + + *result = (uio_sint8) val; + return true; +} + +static inline uio_bool +uio_getU16LE(uio_Stream *stream, uio_uint16 *result) { + uio_uint8 buf[2]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (buf[1] << 8) | buf[0]; + return true; +} + +static inline uio_bool +uio_getU16BE(uio_Stream *stream, uio_uint16 *result) { + uio_uint8 buf[2]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (buf[0] << 8) | buf[1]; + return true; +} + +static inline uio_bool +uio_getS16LE(uio_Stream *stream, uio_sint16 *result) { + uio_uint8 buf[2]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (uio_sint16) ((buf[1] << 8) | buf[0]); + return true; +} + +static inline uio_bool +uio_getS16BE(uio_Stream *stream, uio_sint16 *result) { + uio_uint8 buf[2]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (uio_sint16) ((buf[0] << 8) | buf[1]); + return true; +} + +static inline uio_bool +uio_getU32LE(uio_Stream *stream, uio_uint32 *result) { + uio_uint8 buf[4]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + return true; +} + +static inline uio_bool +uio_getU32BE(uio_Stream *stream, uio_uint32 *result) { + uio_uint8 buf[4]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return true; +} + +static inline uio_bool +uio_getS32LE(uio_Stream *stream, uio_sint32 *result) { + uio_uint8 buf[4]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (uio_sint32) ((buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]); + return true; +} + +static inline uio_bool +uio_getS32BE(uio_Stream *stream, uio_sint32 *result) { + uio_uint8 buf[4]; + + if (uio_fread(buf, sizeof buf, 1, stream) != 1) + return false; + + *result = (uio_sint32) ((buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]); + return true; +} + + +#endif /* LIBS_UIO_GETINT_H_ */ + diff --git a/src/libs/uio/gphys.c b/src/libs/uio/gphys.c new file mode 100644 index 0000000..f25f557 --- /dev/null +++ b/src/libs/uio/gphys.c @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +#define uio_INTERNAL_PHYSICAL +#define uio_INTERNAL_GPHYS +typedef void *uio_NativeHandle; +typedef void *uio_GPRootExtra; +typedef void *uio_GPDirExtra; +typedef void *uio_GPFileExtra; + +#include <errno.h> +#include <stdio.h> + +#include "gphys.h" +#include "paths.h" +#include "uioport.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +static void uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir); +static uio_GPRoot *uio_GPRoot_alloc(void); + +static void uio_GPRoot_free(uio_GPRoot *gPRoot); + +static inline uio_GPDir *uio_GPDir_alloc(void); +void uio_GPDir_delete(uio_GPDir *gPDir); +static inline void uio_GPDir_free(uio_GPDir *gPDir); + +static inline uio_GPFile *uio_GPFile_alloc(void); +void uio_GPFile_delete(uio_GPFile *gPFile); +static inline void uio_GPFile_free(uio_GPFile *gPFile); + +// Call this when you need to edit a file 'dirName' in the GPDir 'gPDir'. +// a new entry is created when necessary. +// uio_gPDirCommitSubDir should be called when you're done with it. +// +// a copy of dirName is made if needed; the caller remains responsible for +// freeing the original +// Allocates a new dir if necessary. +uio_GPDir * +uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName) { + uio_GPDir *subDir; + uio_GPDirEntry *entry; + + entry = uio_GPDirEntries_find(gPDir->entries, dirName); + if (entry != NULL) { + if (uio_GPDirEntry_isDir(entry)) { + // Return existing subdir. + uio_GPDir_ref((uio_GPDir *) entry); + return (uio_GPDir *) entry; + } else { + // There already exists a file with the same name. + // This should not happen within one file system. + fprintf(stderr, "Directory %s shadows file with the same name " + "from the same filesystem.\n", dirName); + } + } + + // return new subdir + subDir = uio_GPDir_new(gPDir->pRoot, NULL, uio_GPDir_DETACHED); + // subDir->ref is initialised at 1 + return subDir; +} + +// call this when you're done with a dir acquired by a call to +// uio_gPDirPrepareSubDir +void +uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName, + uio_GPDir *subDir) { + if (subDir->flags & uio_GPDir_DETACHED) { + // New dir. + // reference to the subDir is passed along to the upDir, + // so subDir->ref should not be changed. + uio_GPDirEntries_add(gPDir->entries, dirName, subDir); + subDir->flags &= ~uio_GPDir_DETACHED; + if (!(subDir->flags & uio_GPDir_PERSISTENT)) { + // Persistent dirs have an extra reference. + uio_GPDir_unref(subDir); + } + } else { + uio_GPDir_unref(subDir); + } +} + +// a copy of fileName is made if needed; the caller remains responsible for +// freeing the original +void +uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName, uio_GPFile *file) { + // A file will never already exist in a dir. There can only be + // one entry in a physical dir with the same name. + uio_GPDirEntries_add(gPDir->entries, fileName, (uio_GPDirEntry *) file); +} + +// Pre: 'fileName' exists in 'gPDir' and is a dir. +void +uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName) { + uio_GPDirEntry *entry; + uio_GPFile *file; + uio_bool retVal; + + entry = uio_GPDirEntries_find(gPDir->entries, fileName); + if (entry == NULL) { + // This means the file has no associated GPFile. + // This can happen when the GPFile structure is only used for caching. + return; + } + + assert(!uio_GPDirEntry_isDir(entry)); + file = (uio_GPFile *) entry; + uio_GPFile_unref(file); + retVal = uio_GPDirEntries_remove(gPDir->entries, fileName); + assert(retVal); +} + +// Pre: 'dirName' exists in 'gPDir' and is a dir. +void +uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName) { + uio_GPDirEntry *entry; + uio_GPDir *subDir; + uio_bool retVal; + + entry = uio_GPDirEntries_find(gPDir->entries, dirName); + if (entry == NULL) { + // This means the directory has no associated GPDir. + // This can happen when the GPDir structure is only used for caching. + return; + } + + assert(uio_GPDirEntry_isDir(entry)); + subDir = (uio_GPDir *) entry; + if (subDir->flags & uio_GPDir_PERSISTENT) { + // Persistent dirs have an extra reference. + uio_GPDir_unref(subDir); + } + retVal = uio_GPDirEntries_remove(gPDir->entries, dirName); + assert(retVal); +} + +void +uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag) { + if (flag) { + gPDir->flags |= uio_GPDir_COMPLETE; + } else + gPDir->flags &= ~uio_GPDir_COMPLETE; +} + +int +uio_GPDir_entryCount(const uio_GPDir *gPDir) { + return uio_GPDirEntries_count(gPDir->entries); +} + +static void +uio_GPDir_access(uio_GPDir *gPDir) { + if (!(gPDir->flags & uio_GPDir_COMPLETE)) + uio_GPDir_fill(gPDir); +} + +// The ref counter for the dir entry is not incremented. +uio_GPDirEntry * +uio_GPDir_getGPDirEntry(uio_GPDir *gPDir, const char *name) { + uio_GPDir_access(gPDir); + return uio_GPDirEntries_find(gPDir->entries, name); +} + +// The ref counter for the dir entry is not incremented. +uio_PDirEntryHandle * +uio_GPDir_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, + const char *name) { + uio_GPDirEntry *gPDirEntry; + + gPDirEntry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (gPDirEntry == NULL) + return NULL; + uio_GPDirEntry_ref(gPDirEntry); + if (uio_GPDirEntry_isDir(gPDirEntry)) { + return (uio_PDirEntryHandle *) uio_PDirHandle_new(pDirHandle->pRoot, + (uio_GPDir *) gPDirEntry); + } else { + return (uio_PDirEntryHandle *) uio_PFileHandle_new(pDirHandle->pRoot, + (uio_GPFile *) gPDirEntry); + } +} + +/* + * Follow a path starting from a specified physical dir as long as possible. + * When you can get no further, 'endGPDir' will be filled in with the dir + * where you ended up, and 'pathRest' will point into the original path. to + * the beginning of the part that was not matched. + * It is allowed to have endGPDir point to gPDir and/or restPath + * point to path when calling this function. + * returns: 0 if the complete path was matched + * ENOENT if some component (the next one) didn't exists + * ENODIR if a component (the next one) exists but isn't a dir + * See also uio_walkPhysicalPath. The difference is that this one works + * directly on the GPDirs and is faster because of that. + */ +int +uio_walkGPPath(uio_GPDir *startGPDir, const char *path, + size_t pathLen, uio_GPDir **endGPDir, const char **pathRest) { + const char *pathEnd; + const char *partStart, *partEnd; + char *tempBuf; + uio_GPDir *gPDir; + uio_GPDirEntry *entry; + int retVal; + + gPDir = startGPDir; + tempBuf = uio_malloc(strlen(path) + 1); + // XXX: Use a dynamically allocated array when moving to C99. + pathEnd = path + pathLen; + getFirstPathComponent(path, pathEnd, &partStart, &partEnd); + while (1) { + if (partStart == pathEnd) { + retVal = 0; + break; + } + memcpy(tempBuf, partStart, partEnd - partStart); + tempBuf[partEnd - partStart] = '\0'; + + entry = uio_GPDir_getGPDirEntry(gPDir, tempBuf); + if (entry == NULL) { + retVal = ENOENT; + break; + } + if (!uio_GPDirEntry_isDir(entry)) { + retVal = ENOTDIR; + break; + } + gPDir = (uio_GPDir *) entry; + getNextPathComponent(pathEnd, &partStart, &partEnd); + } + + uio_free(tempBuf); + *pathRest = partStart; + *endGPDir = gPDir; + return retVal; +} + +uio_GPDirEntries_Iterator * +uio_GPDir_openEntries(uio_PDirHandle *pDirHandle) { + uio_GPDir_access(pDirHandle->extra); + return uio_GPDirEntries_getIterator(pDirHandle->extra->entries); +} + +// the start of 'buf' will be filled with pointers to strings +// those strings are stored elsewhere in buf. +// The function returns the number of strings passed along, or -1 for error. +// If there are no more entries, the last pointer will be NULL. +// (this pointer counts towards the return value) +int +uio_GPDir_readEntries(uio_GPDirEntries_Iterator **iterator, + char *buf, size_t len) { + char *end; + char **start; + int num; + const char *name; + size_t nameLen; + + // buf will be filled like this: + // The start of buf will contain pointers to char *, + // the end will contain the actual char[] that those pointers point to. + // The start and the end will grow towards eachother. + start = (char **) buf; + end = buf + len; + num = 0; + while (!uio_GPDirEntries_iteratorDone(*iterator)) { + name = uio_GPDirEntries_iteratorName(*iterator); + nameLen = strlen(name) + 1; + + // Does this work with systems that need memory access to be + // aligned on a certain number of bytes? + if ((size_t) (sizeof (char *) + nameLen) > + (size_t) (end - (char *) start)) { + // Not enough room to fit the pointer (at the beginning) and + // the string (at the end). + return num; + } + end -= nameLen; + memcpy(end, name, nameLen); + *start = end; + start++; + num++; + *iterator = uio_GPDirEntries_iteratorNext(*iterator); + } + if (sizeof (char *) > (size_t) (end - (char *) start)) { + // not enough room to fit the NULL pointer. + // It will have to be reported seperately the next time. + return num; + } + *start = NULL; + num++; + return num; +} + +void +uio_GPDir_closeEntries(uio_GPDirEntries_Iterator *iterator) { + uio_GPDirEntries_freeIterator(iterator); +} + +void +uio_GPDir_fill(uio_GPDir *gPDir) { + if ((gPDir->flags & uio_GPDir_COMPLETE) && + !(gPDir->flags & uio_GPDir_NOCACHE)) + return; + assert(gPDir->pRoot->extra->ops->fillGPDir != NULL); + gPDir->pRoot->extra->ops->fillGPDir(gPDir); +} + +void +uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot) { + if (gPRoot->extra == NULL) + return; + assert(gPRoot->ops->deleteGPRootExtra != NULL); + gPRoot->ops->deleteGPRootExtra(gPRoot->extra); +} + +void +uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir) { + if (gPDir->extra == NULL) + return; + assert(gPDir->pRoot->extra->ops->deleteGPDirExtra != NULL); + gPDir->pRoot->extra->ops->deleteGPDirExtra(gPDir->extra); +} + +void +uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile) { + if (gPFile->extra == NULL) + return; + assert(gPFile->pRoot->extra->ops->deleteGPFileExtra != NULL); + gPFile->pRoot->extra->ops->deleteGPFileExtra(gPFile->extra); +} + +int +uio_gPDirFlagsFromPRootFlags(int flags) { + int newFlags; + + newFlags = 0; + if (flags & uio_PRoot_NOCACHE) + newFlags |= uio_GPDir_NOCACHE; + + return newFlags; +} + +int +uio_gPFileFlagsFromPRootFlags(int flags) { + int newFlags; + + newFlags = 0; + if (flags & uio_PRoot_NOCACHE) + newFlags |= uio_GPFile_NOCACHE; + + return newFlags; +} + +// This function is to be called from the physical layer. +// uio_GPDirHandle is the extra information for an uio_PDirHandle. +// This is in practice a pointer to the uio_GPDir. +void +uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle) { + uio_GPDir_unref((uio_GPDir *) gPDirHandle); + (void) gPDirHandle; +} + +// This function is to be called from the physical layer. +// uio_GPFileHandle is the extra information for an uio_PFileHandle. +// This is in practice a pointer to the uio_GPFile. +void +uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle) { + uio_GPFile_unref((uio_GPFile *) gPFileHandle); + (void) gPFileHandle; +} + +void +uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry) { + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_GPDir_delete((uio_GPDir *) gPDirEntry); + } else { + uio_GPFile_delete((uio_GPFile *) gPDirEntry); + } +} + +// note: sets ref count to 1 +uio_GPDir * +uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags) { + uio_GPDir *gPDir; + + gPDir = uio_GPDir_alloc(); + gPDir->ref = 1; + gPDir->pRoot = pRoot; + gPDir->entries = uio_GPDirEntries_new(); + gPDir->extra = extra; + flags |= uio_gPDirFlagsFromPRootFlags(gPDir->pRoot->flags); + if (pRoot->extra->flags & uio_GPRoot_PERSISTENT) + flags |= uio_GPDir_PERSISTENT; + gPDir->flags = flags | uio_GPDirEntry_TYPE_DIR; + return gPDir; +} + +// pre: There are no more references to within the gPDir, and +// gPDir is the last reference to the gPDir itself. +void +uio_GPDir_delete(uio_GPDir *gPDir) { +#if 0 + uio_GPDirEntry *entry; + + uio_GPDirEntries_Iterator *iterator; + + iterator = uio_GPDirEntries_getIterator(gPDir->entries); + while (!uio_GPDirEntries_iteratorDone(iterator)) { + entry = uio_GPDirEntries_iteratorItem(iterator); + assert(entry->ref == 0); + if (uio_GPDirEntry_isDir(entry)) { + uio_GPDir_delete((uio_GPDir *) entry); + } else { + uio_GPFile_delete((uio_GPFile *) entry); + } + iterator = uio_GPDirEntries_iteratorNext(iterator); + } +#endif + + assert(gPDir->ref == 0); + uio_GPDirEntries_deleteHashTable(gPDir->entries); + uio_GPDir_deleteGPDirExtra(gPDir); + uio_GPDir_free(gPDir); +} + +static void +uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir) { + uio_GPDirEntry *entry; + uio_GPDirEntries_Iterator *iterator; + + iterator = uio_GPDirEntries_getIterator(gPDir->entries); + while (!uio_GPDirEntries_iteratorDone(iterator)) { + entry = uio_GPDirEntries_iteratorItem(iterator); + if (uio_GPDirEntry_isDir(entry)) { + uio_GPDir_deepPersistentUnref((uio_GPDir *) entry); + } else { + uio_GPFile_unref((uio_GPFile *) entry); + } + iterator = uio_GPDirEntries_iteratorNext(iterator); + } + uio_GPDirEntries_freeIterator(iterator); + if (gPDir->flags & uio_GPDir_PERSISTENT) { + uio_GPDir_unref(gPDir); + } else { + gPDir->flags &= ~uio_GPDir_COMPLETE; + } +} + +static inline uio_GPDir * +uio_GPDir_alloc(void) { + uio_GPDir *result = uio_malloc(sizeof (uio_GPDir)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPDir, (void *) result); +#endif + return result; +} + +static inline void +uio_GPDir_free(uio_GPDir *gPDir) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPDir, (void *) gPDir); +#endif + uio_free(gPDir); +} + +// note: sets ref count to 1 +uio_GPFile * +uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags) { + uio_GPFile *gPFile; + + gPFile = uio_GPFile_alloc(); + gPFile->ref = 1; + gPFile->pRoot = pRoot; + gPFile->extra = extra; + gPFile->flags = flags; + return gPFile; +} + +void +uio_GPFile_delete(uio_GPFile *gPFile) { + assert(gPFile->ref == 0); + uio_GPFile_deleteGPFileExtra(gPFile); + uio_GPFile_free(gPFile); +} + +static inline uio_GPFile * +uio_GPFile_alloc(void) { + uio_GPFile *result = uio_malloc(sizeof (uio_GPFile)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPFile, (void *) result); +#endif + return result; +} + +static inline void +uio_GPFile_free(uio_GPFile *gPFile) { + uio_free(gPFile); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPFile, (void *) gPFile); +#endif +} + +// The ref counter to 'handle' is not incremented. +uio_PRoot * +uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags, + uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags, + uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags) { + uio_PRoot *result; + uio_GPDir *gPTopDir; + uio_GPRoot *gPRoot; + + gPRoot = uio_GPRoot_new(ops, gPRootExtra, gPRootFlags); + result = uio_PRoot_new(NULL, handler, handle, gPRoot, pRootFlags); + + gPTopDir = uio_GPDir_new(result, gPDirExtra, gPDirFlags); + if (gPRoot->flags & uio_GPRoot_PERSISTENT) + uio_GPDir_ref(gPTopDir); + result->rootDir = uio_GPDir_makePDirHandle(gPTopDir); + + return result; +} + +// Pre: there are no more references to PRoot or anything inside it. +int +uio_GPRoot_umount(uio_PRoot *pRoot) { + uio_PDirHandle *topDirHandle; + uio_GPDir *topDir; + + topDirHandle = uio_PRoot_getRootDirHandle(pRoot); + topDir = topDirHandle->extra; + if (pRoot->extra->flags & uio_GPRoot_PERSISTENT) + uio_GPDir_deepPersistentUnref(topDir); + uio_PDirHandle_unref(topDirHandle); + (void) pRoot; + return 0; +} + +uio_GPRoot * +uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra, int flags) { + uio_GPRoot *result; + + result = uio_GPRoot_alloc(); + result->ops = ops; + result->extra = extra; + result->flags = flags; + return result; +} + +void +uio_GPRoot_delete(uio_GPRoot *gPRoot) { + uio_GPRoot_deleteGPRootExtra(gPRoot); + uio_GPRoot_free(gPRoot); +} + +static uio_GPRoot * +uio_GPRoot_alloc(void) { + uio_GPRoot *result = uio_malloc(sizeof (uio_GPRoot)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPRoot, (void *) result); +#endif + return result; +} + +static void +uio_GPRoot_free(uio_GPRoot *gPRoot) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPRoot, (void *) gPRoot); +#endif + uio_free(gPRoot); +} + +// The ref counter to the gPDir is not inremented. +uio_PDirHandle * +uio_GPDir_makePDirHandle(uio_GPDir *gPDir) { + return uio_PDirHandle_new(gPDir->pRoot, gPDir); +} + +#ifdef DEBUG +void +uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry) { + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_GPDir_print(outStream, (uio_GPDir *) gPDirEntry); + } else { + uio_GPFile_print(outStream, (uio_GPFile *) gPDirEntry); + } +} + +void +uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir) { + (void) outStream; + (void) gPDir; +} + +void +uio_GPFile_print(FILE *outStream, uio_GPFile *gPFile) { + (void) outStream; + (void) gPFile; +} +#endif + + diff --git a/src/libs/uio/gphys.h b/src/libs/uio/gphys.h new file mode 100644 index 0000000..7a9d6be --- /dev/null +++ b/src/libs/uio/gphys.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_GPHYS_H_ +#define LIBS_UIO_GPHYS_H_ + +#include "uioport.h" + +#ifndef uio_INTERNAL_PHYSICAL +typedef void *uio_GPRootExtra; +typedef void *uio_GPDirExtra; +typedef void *uio_GPFileExtra; +#endif + +typedef struct CharHashTable_HashTable uio_GPDirEntries; + +#define uio_GPDirEntries_new() \ + ((uio_GPDirEntries *) CharHashTable_newHashTable(NULL, NULL, NULL, \ + NULL, NULL, 0, 0.85, 0.9)) +#define uio_GPDirEntries_add(hashTable, name, item) \ + CharHashTable_add((CharHashTable_HashTable *) hashTable, name, \ + (void *) item) +#define uio_GPDirEntries_remove(hashTable, name) \ + CharHashTable_remove((CharHashTable_HashTable *) hashTable, name) +#define uio_GPDirEntries_count(hashTable) \ + CharHashTable_count((CharHashTable_HashTable *) hashTable) +#define uio_GPDirEntries_find(hashTable, name) \ + ((uio_GPDirEntry *) CharHashTable_find( \ + (CharHashTable_HashTable *) hashTable, name)) +#define uio_GPDirEntries_deleteHashTable(hashTable) \ + CharHashTable_deleteHashTable((CharHashTable_HashTable *) hashTable) +//#define uio_GPDirEntries_clear(hashTable) +// CharHashTable_clear((CharHashTable_HashTable *) hashTable) +#define uio_GPDirEntries_getIterator(hashTable) \ + ((uio_GPDirEntries_Iterator *) CharHashTable_getIterator( \ + (const CharHashTable_HashTable *) hashTable)) +#define uio_GPDirEntries_iteratorDone(iterator) \ + CharHashTable_iteratorDone((const CharHashTable_Iterator *) iterator) +#define uio_GPDirEntries_iteratorName(iterator) \ + CharHashTable_iteratorKey((CharHashTable_Iterator *) iterator) +#define uio_GPDirEntries_iteratorItem(iterator) \ + ((uio_GPDirEntry *) CharHashTable_iteratorValue( \ + (CharHashTable_Iterator *) iterator)) +#define uio_GPDirEntries_iteratorNext(iterator) \ + ((uio_GPDirEntries_Iterator *) CharHashTable_iteratorNext( \ + (CharHashTable_Iterator *) iterator)) +#define uio_GPDirEntries_freeIterator(iterator) \ + CharHashTable_freeIterator(iterator) + +// 'forward' declarations +typedef struct uio_GPDirEntry uio_GPDirEntry; +typedef struct uio_GPDir uio_GPDir; +typedef struct uio_GPFile uio_GPFile; +typedef struct uio_GPRoot_Operations uio_GPRoot_Operations; +typedef struct uio_GPRoot uio_GPRoot; + +#include "charhashtable.h" +typedef CharHashTable_Iterator uio_GPDirEntries_Iterator; + +#ifdef uio_INTERNAL_GPHYS +typedef uio_GPDirEntries_Iterator *uio_NativeEntriesContext; +#endif +typedef struct uio_GPRoot *uio_PRootExtra; +typedef struct uio_GPDir uio_GPDirHandle; +typedef uio_GPDirHandle *uio_PDirHandleExtra; +typedef struct uio_GPFile uio_GPFileHandle; +typedef uio_GPFileHandle *uio_PFileHandleExtra; + + +#ifdef DEBUG +# include <stdio.h> +#endif +#include "iointrn.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +struct uio_GPRoot_Operations { + void (*fillGPDir)(uio_GPDir *); + void (*deleteGPRootExtra)(uio_GPRootExtra); + void (*deleteGPDirExtra)(uio_GPDirExtra); + void (*deleteGPFileExtra)(uio_GPFileExtra); +}; + +struct uio_GPRoot { + int flags; +#define uio_GPRoot_PERSISTENT 0x4000 + /* Set if directories in this file system should not be deleted + * as long as the file system is mounted. If this flag is not + * set, the GPDir structure is only a cache. + */ + uio_GPRoot_Operations *ops; + uio_GPRootExtra extra; +}; + +#define uio_GPDirEntry_COMMON \ + int flags; \ + int ref; \ + /* Number of times this structure is referenced from the \ + * outside (so not counting the references from subdirs \ + * or files when the entry is a directory) \ + */ + +#define uio_GPDirEntry_NOCACHE uio_PRoot_NOCACHE + +/* + * uio_GPDirEntry + * super-'class' of uio_GPDir and uio_GPFile + */ +struct uio_GPDirEntry { + uio_GPDirEntry_COMMON + void *extra; +}; + +#define uio_GPDirEntry_TYPE_REG 0x0000 +#define uio_GPDirEntry_TYPE_DIR 0x0001 +#define uio_GPDirEntry_TYPEMASK 0x0001 + +/* + * uio_GPDir + * Represents a directory in a physical directory structure. + * sub-'class' of uio_GPDirEntry + */ +struct uio_GPDir { + uio_GPDirEntry_COMMON +# define uio_GPDir_NOCACHE uio_GPDirEntry_NOCACHE + /* This directory info will not be cached. + * PDIR_COMPLETE is irrelevant in this case */ +# define uio_GPDir_COMPLETE 0x1000 + /* Set if fillDir should not be called if an entry does not + * exist in a directory. Usually set if the entire dir has been + * completely read in. + */ +# define uio_GPDir_DETACHED 0x2000 + /* Set if this dir is not linked to from elsewhere in the physical + * structure */ +# define uio_GPDir_PERSISTENT 0x4000 + /* Set if this dir should not be deleted as long as the file + * system is mounted. If this flag is not set, the GPDir + * structure is only a cache. + */ + uio_GPDirExtra extra; + /* extra internal data for some filesystem types */ + uio_PRoot *pRoot; + uio_GPDirEntries *entries; +}; + + +/* + * uio_GPFile + * Represents a file in a physical directory structure. + * sub-'class' of uio_GPDirEntry + */ +struct uio_GPFile { + uio_GPDirEntry_COMMON +# define uio_GPFile_NOCACHE uio_GPDirEntry_NOCACHE + /* Info on this file will not be cached. */ + uio_GPFileExtra extra; + /* extra internal data for some filesystem types */ + uio_PRoot *pRoot; +}; + + +static inline uio_bool +uio_GPDirEntry_isReg(uio_GPDirEntry *gPDirEntry) { + return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) == + uio_GPDirEntry_TYPE_REG; +} + +static inline uio_bool +uio_GPDirEntry_isDir(uio_GPDirEntry *gPDirEntry) { + return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) == + uio_GPDirEntry_TYPE_DIR; +} + + +#ifdef DEBUG +void uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry); +void uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir); +void uio_GPFile_print(FILE *outStream, uio_GPFile *pFile); +#endif + +uio_NativeEntriesContext uio_GPDir_openEntries(uio_PDirHandle *pDirHandle); +int uio_GPDir_readEntries(uio_NativeEntriesContext *iterator, + char *buf, size_t len); +void uio_GPDir_closeEntries(uio_NativeEntriesContext iterator); +int uio_GPDir_entryCount(const uio_GPDir *gPDir); + +int uio_gPDirFlagsFromPRootFlags(int flags); +int uio_gPFileFlagsFromPRootFlags(int flags); +uio_PRoot *uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags, + uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags, + uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags); +int uio_GPRoot_umount(uio_PRoot *pRoot); + +uio_GPDir *uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName); +void uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName, + uio_GPDir *subDir); +void uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName, + uio_GPFile *file); +void uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName); +void uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName); +void uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag); +uio_GPDirEntry *uio_GPDir_getGPDirEntry(uio_GPDir *gPDir, + const char *name); +uio_PDirEntryHandle *uio_GPDir_getPDirEntryHandle( + const uio_PDirHandle *pDirHandle, const char *name); +int uio_walkGPPath(uio_GPDir *startGPDir, const char *path, + size_t pathLen, uio_GPDir **endGPDir, const char **pathRest); +uio_PDirHandle *uio_GPDir_makePDirHandle(uio_GPDir *gPDir); + +void uio_GPDir_fill(uio_GPDir *gPDir); +void uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot); +void uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir); +void uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile); + +void uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle); +void uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle); +void uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry); +uio_GPRoot *uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra, + int flags); +void uio_GPRoot_delete(uio_GPRoot *gPRoot); +uio_GPDir *uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags); +void uio_GPDir_delete(uio_GPDir *gPDir); +uio_GPFile *uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags); +void uio_GPFile_delete(uio_GPFile *gPFile); + + +static inline void +uio_GPDirEntry_ref(uio_GPDirEntry *gPDirEntry) { +#ifdef uio_MEM_DEBUG + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_MemDebug_debugRef(uio_GPDir, (void *) gPDirEntry); + } else { + uio_MemDebug_debugRef(uio_GPFile, (void *) gPDirEntry); + } +#endif + gPDirEntry->ref++; +} + +static inline void +uio_GPDirEntry_unref(uio_GPDirEntry *gPDirEntry) { + assert(gPDirEntry->ref > 0); +#ifdef uio_MEM_DEBUG + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDirEntry); + } else { + uio_MemDebug_debugUnref(uio_GPFile, (void *) gPDirEntry); + } +#endif + gPDirEntry->ref--; + if (gPDirEntry->ref == 0) + uio_GPDirEntry_delete(gPDirEntry); +} + +static inline void +uio_GPDir_ref(uio_GPDir *gPDir) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugRef(uio_GPDir, (void *) gPDir); +#endif + gPDir->ref++; +} + +static inline void +uio_GPDir_unref(uio_GPDir *gPDir) { + assert(gPDir->ref > 0); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDir); +#endif + gPDir->ref--; + if (gPDir->ref == 0) + uio_GPDir_delete(gPDir); +} + +static inline void +uio_GPFile_ref(uio_GPFile *gPFile) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugRef(uio_GPFile, (void *) gPFile); +#endif + gPFile->ref++; +} + +static inline void +uio_GPFile_unref(uio_GPFile *gPFile) { + assert(gPFile->ref > 0); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugUnref(uio_GPFile, (void *) gPFile); +#endif + gPFile->ref--; + if (gPFile->ref == 0) + uio_GPFile_delete(gPFile); +} + + +#endif /* LIBS_UIO_GPHYS_H_ */ + diff --git a/src/libs/uio/hashtable.c b/src/libs/uio/hashtable.c new file mode 100644 index 0000000..1f376e1 --- /dev/null +++ b/src/libs/uio/hashtable.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 HASHTABLE_INTERNAL + // If HASHTABLE_INTERNAL is already defined, this file is included + // as a template. In this case hashtable.h has already been included. +# define HASHTABLE_INTERNAL +# include "hashtable.h" +#endif + +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <math.h> + +#include "mem.h" +#include "uioport.h" + +static void HASHTABLE_(setup)(HASHTABLE_(HashTable) *HashTable, + uio_uint32 size); +static void HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable); +static inline uio_uint32 nextPower2(uio_uint32 x); + +static inline HASHTABLE_(HashTable) *HASHTABLE_(allocHashTable)(void); +static inline HASHTABLE_(HashEntry) *HASHTABLE_(newHashEntry)(uio_uint32 hash, + HASHTABLE_(Key) *key, HASHTABLE_(Value) *value, + HASHTABLE_(HashEntry) *next); +static inline HASHTABLE_(HashEntry) *HASHTABLE_(allocHashEntry)(void); +static inline void HASHTABLE_(freeHashEntry)( + HASHTABLE_(HashEntry) *entry); + +// Create a new HashTable. +HASHTABLE_(HashTable) * +HASHTABLE_(newHashTable)( + HASHTABLE_(HashFunction) hashFunction, + HASHTABLE_(EqualFunction) equalFunction, + HASHTABLE_(CopyFunction) copyFunction, + HASHTABLE_(FreeKeyFunction) freeKeyFunction, + HASHTABLE_(FreeValueFunction) freeValueFunction, + uio_uint32 initialSize, + double minFillQuotient, + double maxFillQuotient) { + HASHTABLE_(HashTable) *hashTable; + + assert(maxFillQuotient >= minFillQuotient); + + hashTable = HASHTABLE_(allocHashTable)(); + hashTable->hashFunction = hashFunction; + hashTable->equalFunction = equalFunction; + hashTable->copyFunction = copyFunction; + hashTable->freeKeyFunction = freeKeyFunction; + hashTable->freeValueFunction = freeValueFunction; + + hashTable->minFillQuotient = minFillQuotient; + hashTable->maxFillQuotient = maxFillQuotient; + HASHTABLE_(setup)(hashTable, initialSize); + + return hashTable; +} + +// Add an entry to the HashTable. +uio_bool +HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable, + const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value) { + uio_uint32 hash; + struct HASHTABLE_(HashEntry) *entry; + + hash = HASHTABLE_(HASH)(hashTable, key); + entry = hashTable->entries[hash & hashTable->hashMask]; + while (entry != NULL) { + if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) { + // key is already present + return false; + } + entry = entry->next; + } + +#ifdef HashTable_PROFILE + if (hashTable->entries[hash & hashTable->hashMask] != NULL) + hashTable->numCollisions++; +#endif + hashTable->entries[hash & hashTable->hashMask] = + HASHTABLE_(newHashEntry)(hash, + HASHTABLE_(COPY)(hashTable, key), value, + hashTable->entries[hash & hashTable->hashMask]); + + hashTable->numEntries++; + if (hashTable->numEntries > hashTable->maxSize) + HASHTABLE_(resize)(hashTable); + + return true; +} + +// Remove an entry with a specified Key from the HashTable. +uio_bool +HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable, + const HASHTABLE_(Key) *key) { + uio_uint32 hash; + struct HASHTABLE_(HashEntry) **entry, *next; + + hash = HASHTABLE_(HASH)(hashTable, key); + entry = &hashTable->entries[hash & hashTable->hashMask]; + while (1) { + if (*entry == NULL) + return false; + if (HASHTABLE_(EQUAL)(hashTable, key, (*entry)->key)) { + // found the key + break; + } + entry = &(*entry)->next; + } + next = (*entry)->next; + HASHTABLE_(FREEKEY)(hashTable, (*entry)->key); + HASHTABLE_(FREEVALUE)(hashTable, (*entry)->value); + HASHTABLE_(freeHashEntry)(*entry); + *entry = next; + + hashTable->numEntries--; + if (hashTable->numEntries < hashTable->minSize) + HASHTABLE_(resize)(hashTable); + + return true; +} + +// Find the Value stored for some Key. +HASHTABLE_(Value) * +HASHTABLE_(find)(HASHTABLE_(HashTable) *hashTable, + const HASHTABLE_(Key) *key) { + uio_uint32 hash; + struct HASHTABLE_(HashEntry) *entry; + + hash = HASHTABLE_(HASH)(hashTable, key); + entry = hashTable->entries[hash & hashTable->hashMask]; + while (entry != NULL) { + if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) { + // found the key + return entry->value; + } + entry = entry->next; + } + return NULL; +} + +// Returns the number of entries in the HashTable. +uio_uint32 +HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable) { + return hashTable->numEntries; +} + +// Auxiliary function to (re)initialise the buckets in the HashTable. +static void +HASHTABLE_(setup)(HASHTABLE_(HashTable) *hashTable, uio_uint32 initialSize) { + if (initialSize < 4) + initialSize = 4; + hashTable->size = nextPower2(ceil(((double) initialSize) / + hashTable->maxFillQuotient)); + hashTable->hashMask = hashTable->size - 1; + hashTable->minSize = ceil(((double) (hashTable->size >> 1)) + * hashTable->minFillQuotient); + hashTable->maxSize = floor(((double) hashTable->size) + * hashTable->maxFillQuotient); + hashTable->entries = uio_calloc(hashTable->size, + sizeof (HASHTABLE_(HashEntry) *)); + hashTable->numEntries = 0; +#ifdef HashTable_PROFILE + hashTable->numCollisions = 0; +#endif +} + +// Resize the buckets in the HashTable. +static void +HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable) { + HASHTABLE_(HashEntry) **oldEntries; + HASHTABLE_(HashEntry) *entry, *next; + HASHTABLE_(HashEntry) **newLocation; + uio_uint32 oldNumEntries; + uio_uint32 i; + + oldNumEntries = hashTable->numEntries; + oldEntries = hashTable->entries; + + HASHTABLE_(setup)(hashTable, hashTable->numEntries); + hashTable->numEntries = oldNumEntries; + + i = 0; + while (oldNumEntries > 0) { + entry = oldEntries[i]; + while (entry != NULL) { + next = entry->next; + newLocation = &hashTable->entries[entry->hash & + hashTable->hashMask]; +#ifdef HashTable_PROFILE + if (*newLocation != NULL) + hashTable->numCollisions++; +#endif + entry->next = *newLocation; + *newLocation = entry; + oldNumEntries--; + entry = next; + } + i++; + } + + uio_free(oldEntries); +} + +// Adapted from "Hackers Delight" +// Returns the smallest power of two greater or equal to x. +static inline uio_uint32 +nextPower2(uio_uint32 x) { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +// Get an iterator to iterate through all the entries in the HashTable. +// NB: Iterator should be considered invalid if the HashTable is changed. +// TODO: change this (make it thread-safe) +// this can be done by only marking items as deleted when +// there are outstanding iterators. +HASHTABLE_(Iterator) * +HASHTABLE_(getIterator)(const HASHTABLE_(HashTable) *hashTable) { + HASHTABLE_(Iterator) *iterator; + uio_uint32 i; + + iterator = uio_malloc(sizeof (HASHTABLE_(Iterator))); + iterator->hashTable = hashTable; + + // Look for the first used bucket. + for (i = 0; i < iterator->hashTable->size; i++) { + if (iterator->hashTable->entries[i] != NULL) { + // Found a used bucket. + iterator->bucketNr = i; + iterator->entry = iterator->hashTable->entries[i]; + return iterator; + } + } + + // No entries were found. + iterator->bucketNr = i; + iterator->entry = NULL; + return iterator; +} + +// Returns true if and only if there are no more entries in the hash table +// for the Iterator to find. +int +HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator) { + return iterator->bucketNr >= iterator->hashTable->size; +} + +// Get the Key of the entry pointed to by an Iterator. +HASHTABLE_(Key) * +HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator) { + return iterator->entry->key; +} + +// Get the Value of the entry pointed to by an Iterator. +HASHTABLE_(Value) * +HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator) { + return iterator->entry->value; +} + +// Move the Iterator to the next entry in the HashTable. +// Should not be called if the iterator is already past the last entry. +HASHTABLE_(Iterator) * +HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator) { + uio_uint32 i; + + // If there's another entry in this bucket, use that. + iterator->entry = iterator->entry->next; + if (iterator->entry != NULL) + return iterator; + + // Look for the next used bucket. + for (i = iterator->bucketNr + 1; i < iterator->hashTable->size; i++) { + if (iterator->hashTable->entries[i] != NULL) { + // Found another used bucket. + iterator->bucketNr = i; + iterator->entry = iterator->hashTable->entries[i]; + return iterator; + } + } + + // No more entries were found. + iterator->bucketNr = i; + iterator->entry = NULL; + return iterator; +} + +// Free the Iterator. +void +HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator) { + uio_free(iterator); +} + +// Auxiliary function to allocate a HashTable. +static inline HASHTABLE_(HashTable) * +HASHTABLE_(allocHashTable)(void) { + return uio_malloc(sizeof (HASHTABLE_(HashTable))); +} + +// Auxiliary function to create a HashEntry. +static inline HASHTABLE_(HashEntry) * +HASHTABLE_(newHashEntry)(uio_uint32 hash, HASHTABLE_(Key) *key, + HASHTABLE_(Value) *value, HASHTABLE_(HashEntry) *next) { + HASHTABLE_(HashEntry) *result; + + result = HASHTABLE_(allocHashEntry)(); + result->hash = hash; + result->key = key; + result->value = value; + result->next = next; + return result; +} + +// Allocate a new HashEntry. +static inline HASHTABLE_(HashEntry) * +HASHTABLE_(allocHashEntry)(void) { + return uio_malloc(sizeof (HASHTABLE_(HashEntry))); +} + +// Delete the HashTable. +void +HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable) { + uio_uint32 i; + HASHTABLE_(HashEntry) *entry, *next; + HASHTABLE_(HashEntry) **bucketPtr; + + i = hashTable->numEntries; + bucketPtr = hashTable->entries; + while (i > 0) { + entry = *bucketPtr; + while (entry != NULL) { + next = entry->next; + HASHTABLE_(FREEKEY)(hashTable, entry->key); + HASHTABLE_(FREEVALUE)(hashTable, entry->value); + HASHTABLE_(freeHashEntry)(entry); + entry = next; + i--; + } + bucketPtr++; + } + uio_free(hashTable->entries); + uio_free(hashTable); +} + +// Auxiliary function to deallocate a HashEntry. +static inline void +HASHTABLE_(freeHashEntry)(HASHTABLE_(HashEntry) *entry) { + uio_free(entry); +} + diff --git a/src/libs/uio/hashtable.h b/src/libs/uio/hashtable.h new file mode 100644 index 0000000..eb4437f --- /dev/null +++ b/src/libs/uio/hashtable.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +// The 'already included' check must be done slightly more complicated +// than usually. This file may be included directly only once, +// but it may be included my derivative HashTable definitions that use +// this file as a template more than once. +#if !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC) +#if defined(HASHTABLE_) +# define HASHTABLE_GENERIC +#endif + +#include "types.h" +#include "uioport.h" + +// Define to enable profiling. +#define HashTable_PROFILE + +// You can use inline hash functions for extra speed, by using this file as +// a template. +// To do this, make a new .h and .c file. In the .h file, define the macros +// (and typedefs) from the HASHTABLE_ block below. +// In the .c file, #define HASHTABLE_INTERNAL, #include the .h file +// and hashtable.c (in this order), and add the necessary functions. +// If you do not need to free the Value, you can define HashTable_FREEVALUE +// as a no-op. +#ifndef HASHTABLE_ +# define HASHTABLE_(identifier) HashTable ## _ ## identifier + typedef void HashTable_Key; + typedef void HashTable_Value; +# define HashTable_HASH(hashTable, hashValue) \ + (hashTable)->hashFunction(hashValue) +# define HashTable_EQUAL(hashTable, hashKey1, hashKey2) \ + (hashTable)->equalFunction(hashKey1, hashKey2) +# define HashTable_COPY(hashTable, hashKey) \ + (hashTable)->copyFunction(hashKey) +# define HashTable_FREEKEY(hashTable, hashKey) \ + (hashTable)->freeKeyFunction(hashKey) +# define HashTable_FREEVALUE(hashTable, hashValue) \ + (hashTable)->freeValueFunction(hashValue) +#endif + + + +typedef uio_uint32 (*HASHTABLE_(HashFunction))(const HASHTABLE_(Key) *); +typedef uio_bool (*HASHTABLE_(EqualFunction))(const HASHTABLE_(Key) *, + const HASHTABLE_(Key) *); +typedef HASHTABLE_(Value) *(*HASHTABLE_(CopyFunction))( + const HASHTABLE_(Key) *); +typedef void (*HASHTABLE_(FreeKeyFunction))(HASHTABLE_(Key) *); +typedef void (*HASHTABLE_(FreeValueFunction))(HASHTABLE_(Value) *); + +typedef struct HASHTABLE_(HashTable) HASHTABLE_(HashTable); +typedef struct HASHTABLE_(HashEntry) HASHTABLE_(HashEntry); +typedef struct HASHTABLE_(Iterator) HASHTABLE_(Iterator); + +struct HASHTABLE_(HashTable) { + HASHTABLE_(HashFunction) hashFunction; + // Function creating a uio_uint32 hash of a key. + HASHTABLE_(EqualFunction) equalFunction; + // Function used to compare two keys. + HASHTABLE_(CopyFunction) copyFunction; + // Function used to copy a key. + HASHTABLE_(FreeKeyFunction) freeKeyFunction; + // Function used to free a key. + HASHTABLE_(FreeValueFunction) freeValueFunction; + // Function used to free a value. Called when an entry is + // removed using the remove function, or for entries + // still in the HashTable when the HashTable is deleted. + + double minFillQuotient; + // How much of half of the hashtable needs to be filled + // before resizing to size/2. + double maxFillQuotient; + // How much of the hashTable needs to be filled before + // resizing to size*2. + uio_uint32 minSize; + // Resize to size/2 when below this size. + uio_uint32 maxSize; + // Resize to size*2 when above this size. + uio_uint32 size; + // The number of buckets in the hash table. + uio_uint32 hashMask; + // Mask to take on a the calculated hash value, to make it + // fit into the table. + + HASHTABLE_(HashEntry) **entries; + // The actual entries + + uio_uint32 numEntries; +#ifdef HashTable_PROFILE + uio_uint32 numCollisions; +#endif +}; + +struct HASHTABLE_(HashEntry) { + uio_uint32 hash; + HASHTABLE_(Key) *key; + HASHTABLE_(Value) *value; + HASHTABLE_(HashEntry) *next; +}; + +struct HASHTABLE_(Iterator) { + const HASHTABLE_(HashTable) *hashTable; + uio_uint32 bucketNr; + HASHTABLE_(HashEntry) *entry; +}; + +HASHTABLE_(HashTable) *HASHTABLE_(newHashTable)( + HASHTABLE_(HashFunction) hashFunction, + HASHTABLE_(EqualFunction) equalFunction, + HASHTABLE_(CopyFunction) copyFunction, + HASHTABLE_(FreeKeyFunction) freeKeyFunction, + HASHTABLE_(FreeValueFunction) freeValueFunction, + uio_uint32 initialSize, + double minFillQuotient, double maxFillQuotient); +uio_bool HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable, + const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value); +uio_bool HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable, + const HASHTABLE_(Key) *key); +HASHTABLE_(Value) *HASHTABLE_(find)( + HASHTABLE_(HashTable) *hashTable, const HASHTABLE_(Key) *key); +uio_uint32 HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable); +void HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable); +HASHTABLE_(Iterator) *HASHTABLE_(getIterator)( + const HASHTABLE_(HashTable) *hashTable); +int HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator); +HASHTABLE_(Key) *HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator); +HASHTABLE_(Value) *HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator); +HASHTABLE_(Iterator) *HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator); +void HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator); + +#ifndef HASHTABLE_INTERNAL +# undef HASHTABLE_ +#endif + +#endif /* !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC) */ + + + diff --git a/src/libs/uio/io.c b/src/libs/uio/io.c new file mode 100644 index 0000000..247d1e2 --- /dev/null +++ b/src/libs/uio/io.c @@ -0,0 +1,1859 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <errno.h> +#include <fcntl.h> +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "iointrn.h" +#include "ioaux.h" +#include "mount.h" +#include "fstypes.h" +#include "mounttree.h" +#include "physical.h" +#include "paths.h" +#include "mem.h" +#include "uioutils.h" +#include "uioport.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +#if 0 +static int uio_accessDir(uio_DirHandle *dirHandle, const char *path, + int mode); +#endif +static int uio_statDir(uio_DirHandle *dirHandle, const char *path, + struct stat *statBuf); +static int uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf); + +static void uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[], + int numPDirHandles); + +static inline uio_PDirHandle *uio_PDirHandle_alloc(void); +static inline void uio_PDirHandle_free(uio_PDirHandle *pDirHandle); +static inline uio_PFileHandle *uio_PFileHandle_alloc(void); +static inline void uio_PFileHandle_free(uio_PFileHandle *pFileHandle); + +static uio_DirHandle *uio_DirHandle_new(uio_Repository *repository, char *path, + char *rootEnd); +static inline uio_DirHandle *uio_DirHandle_alloc(void); +static inline void uio_DirHandle_free(uio_DirHandle *dirHandle); + +static inline uio_Handle *uio_Handle_alloc(void); +static inline void uio_Handle_free(uio_Handle *handle); + +static uio_MountHandle *uio_MountHandle_new(uio_Repository *repository, + uio_MountInfo *mountInfo); +static inline void uio_MountHandle_delete(uio_MountHandle *mountHandle); +static inline uio_MountHandle *uio_MountHandle_alloc(void); +static inline void uio_MountHandle_free(uio_MountHandle *mountHandle); + + + +void +uio_init(void) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_init(); +#endif + uio_registerDefaultFileSystems(); +} + +void +uio_unInit(void) { + uio_unRegisterDefaultFileSystems(); +#ifdef uio_MEM_DEBUG +# ifdef DEBUG + uio_MemDebug_printPointers(stderr); + fflush(stderr); +# endif + uio_MemDebug_unInit(); +#endif +} + +uio_Repository * +uio_openRepository(int flags) { + return uio_Repository_new(flags); +} + +void +uio_closeRepository(uio_Repository *repository) { + uio_unmountAllDirs(repository); + uio_Repository_unref(repository); +} + +/* + * Function name: uio_mountDir + * Description: Grafts a directory from inside a physical fileSystem + * into the locical filesystem, at a specified directory. + * Arguments: destRep - the repository where the newly mounted dir + * is to be grafted. + * mountPoint - the path to the directory where the dir + * is to be grafted. + * fsType - the file system type of physical fileSystem + * pointed to by sourcePath. + * sourceDir - the directory to which 'sourcePath' is to + * be taken relative. + * sourcePath - a path relative to sourceDir, which contains + * the file/directory to be mounted. + * If sourceDir and sourcePath are NULL, the file + * system of the operating system will be used. + * inPath - the location relative to the root of the newly + * mounted fileSystem, pointing to the directory + * that is to be grafted. + * Note: If fsType is uio_FSTYPE_STDIO, inPath is + * relative to the root of the filesystem, NOT to + * the current working dir. + * autoMount - array of automount options in function + * in this mountPoint. + * flags - one of uio_MOUNT_TOP, uio_MOUNT_BOTTOM, + * uio_MOUNT_BELOW, uio_MOUNT_ABOVE, specifying + * the precedence of this mount, OR'ed with + * one or more of the following flags: + * uio_MOUNT_RDONLY (no writing is allowed) + * relative - If 'flags' includes uio_MOUNT_BELOW or + * uio_MOUNT_ABOVE, this is the mount handle + * where the new mount is relative to. + * Otherwise, it should be NULL. + * Returns: a handle suitable for uio_unmountDir() + * NULL if an error occured. In this case 'errno' is set. + */ +uio_MountHandle * +uio_mountDir(uio_Repository *destRep, const char *mountPoint, + uio_FileSystemID fsType, + uio_DirHandle *sourceDir, const char *sourcePath, + const char *inPath, uio_AutoMount **autoMount, int flags, + uio_MountHandle *relative) { + uio_PRoot *pRoot; + uio_Handle *handle; + uio_FileSystemHandler *handler; + uio_MountInfo *relativeInfo; + + switch (flags & uio_MOUNT_LOCATION_MASK) { + case uio_MOUNT_TOP: + case uio_MOUNT_BOTTOM: + if (relative != NULL) { + errno = EINVAL; + return NULL; + } + relativeInfo = NULL; + break; + case uio_MOUNT_BELOW: + case uio_MOUNT_ABOVE: + if (relative == NULL) { + errno = EINVAL; + return NULL; + } + relativeInfo = relative->mountInfo; + break; + default: + abort(); + } + + if (mountPoint[0] == '/') + mountPoint++; + if (!validPathName(mountPoint, strlen(mountPoint))) { + errno = EINVAL; + return NULL; + } + + // TODO: check if the filesystem is already mounted, and if so, reuse it. + // A RO filedescriptor will need to be replaced though if the + // filesystem needs to be remounted RW now. + if (sourceDir == NULL) { + if (sourcePath != NULL) { + // bad: sourceDir is NULL, but sourcePath isn't + errno = EINVAL; + return NULL; + } + handle = NULL; + } else { + if (sourcePath == NULL) { + // bad: sourcePath is NULL, but sourceDir isn't + errno = EINVAL; + return NULL; + } + handle = uio_open(sourceDir, sourcePath, + ((flags & uio_MOUNT_RDONLY) == uio_MOUNT_RDONLY ? + O_RDONLY : O_RDWR) +#ifdef WIN32 + | O_BINARY +#endif + , 0); + if (handle == NULL) { + // errno is set + return NULL; + } + } + + handler = uio_getFileSystemHandler(fsType); + if (handler == NULL) { + if (handle) + uio_close(handle); + errno = ENODEV; + return NULL; + } + + assert(handler->mount != NULL); + pRoot = (handler->mount)(handle, flags); + if (pRoot == NULL) { + int savedErrno; + + savedErrno = errno; + if (handle) + uio_close(handle); + errno = savedErrno; + return NULL; + } + + if (handle) { + // Close this reference to handle. + // The physical layer may store the link in pRoot, in which it + // will be cleaned up from uio_unmount(). + uio_close(handle); + } + + // The new file system is ready, now we need to find the specified + // dir inside it and put it in its place in the mountTree. + { + uio_PDirHandle *endDirHandle; + const char *endInPath; + char *dirName; + uio_MountInfo *mountInfo; + uio_MountTree *mountTree; + uio_PDirHandle *pRootHandle; +#ifdef BACKSLASH_IS_PATH_SEPARATOR + char *unixPath; + + unixPath = dosToUnixPath(inPath); + inPath = unixPath; +#endif /* BACKSLASH_IS_PATH_SEPARATOR */ + + if (inPath[0] == '/') + inPath++; + pRootHandle = uio_PRoot_getRootDirHandle(pRoot); + uio_walkPhysicalPath(pRootHandle, inPath, strlen(inPath), + &endDirHandle, &endInPath); + if (*endInPath != '\0') { + // Path inside the filesystem to mount does not exist. +#ifdef BACKSLASH_IS_PATH_SEPARATOR + uio_free(unixPath); +#endif /* BACKSLASH_IS_PATH_SEPARATOR */ + uio_PDirHandle_unref(endDirHandle); + uio_PRoot_unrefMount(pRoot); + errno = ENOENT; + return NULL; + } + + dirName = uio_malloc(endInPath - inPath + 1); + memcpy(dirName, inPath, endInPath - inPath); + dirName[endInPath - inPath] = '\0'; +#ifdef BACKSLASH_IS_PATH_SEPARATOR + // InPath is a copy with the paths fixed. + uio_free(unixPath); +#endif /* BACKSLASH_IS_PATH_SEPARATOR */ + mountInfo = uio_MountInfo_new(fsType, NULL, endDirHandle, dirName, + autoMount, NULL, flags); + uio_repositoryAddMount(destRep, mountInfo, + flags & uio_MOUNT_LOCATION_MASK, relativeInfo); + mountTree = uio_mountTreeAddMountInfo(destRep, destRep->mountTree, + mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK, + relativeInfo); + // mountTree is the node in destRep->mountTree where mountInfo + // leads to. + mountInfo->mountTree = mountTree; + mountInfo->mountHandle = uio_MountHandle_new(destRep, mountInfo); + return mountInfo->mountHandle; + } +} + +// Mount a repository directory into same repository at a different location +// From fossil. +uio_MountHandle * +uio_transplantDir(const char *mountPoint, uio_DirHandle *sourceDir, int flags, + uio_MountHandle *relative) { + uio_MountInfo *relativeInfo; + int numPDirHandles; + uio_PDirHandle **pDirHandles; + uio_MountTreeItem **treeItems; + int i; + uio_MountHandle *handle = NULL; + + if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) { + // Only read-only transplants supported atm + errno = ENOSYS; + return NULL; + } + + switch (flags & uio_MOUNT_LOCATION_MASK) { + case uio_MOUNT_TOP: + case uio_MOUNT_BOTTOM: + if (relative != NULL) { + errno = EINVAL; + return NULL; + } + relativeInfo = NULL; + break; + case uio_MOUNT_BELOW: + case uio_MOUNT_ABOVE: + if (relative == NULL) { + errno = EINVAL; + return NULL; + } + relativeInfo = relative->mountInfo; + break; + default: + abort(); + } + + if (mountPoint[0] == '/') + mountPoint++; + if (!validPathName(mountPoint, strlen(mountPoint))) { + errno = EINVAL; + return NULL; + } + + if (uio_getPathPhysicalDirs(sourceDir, "", 0, + &pDirHandles, &numPDirHandles, &treeItems) == -1) { + // errno is set + return NULL; + } + if (numPDirHandles == 0) { + errno = ENOENT; + return NULL; + } + + // TODO: We only transplant the first read-only physical dir that we find + // Maybe transplant all of them? We would then have several + // uio_MountHandles to return. + for (i = 0; i < numPDirHandles; ++i) { + uio_PDirHandle *pDirHandle = pDirHandles[i]; + uio_MountInfo *oldMountInfo = treeItems[i]->mountInfo; + uio_Repository *rep = oldMountInfo->mountHandle->repository; + uio_MountInfo *mountInfo; + uio_MountTree *mountTree; + + // Only interested in read-only dirs in this incarnation + if (!uio_mountInfoIsReadOnly(oldMountInfo)) + continue; + + mountInfo = uio_MountInfo_new(oldMountInfo->fsID, NULL, pDirHandle, + uio_strdup(""), oldMountInfo->autoMount, NULL, flags); + // New mount references the same handles + uio_PDirHandle_ref(pDirHandle); + uio_PRoot_refMount(pDirHandle->pRoot); + + uio_repositoryAddMount(rep, mountInfo, + flags & uio_MOUNT_LOCATION_MASK, relativeInfo); + mountTree = uio_mountTreeAddMountInfo(rep, rep->mountTree, + mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK, + relativeInfo); + // mountTree is the node in rep->mountTree where mountInfo leads to + mountInfo->mountTree = mountTree; + mountInfo->mountHandle = uio_MountHandle_new(rep, mountInfo); + handle = mountInfo->mountHandle; + break; + } + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + uio_free(treeItems); + + if (handle == NULL) + errno = ENOENT; + + return handle; +} + +int +uio_unmountDir(uio_MountHandle *mountHandle) { + uio_PRoot *pRoot; + + pRoot = mountHandle->mountInfo->pDirHandle->pRoot; + + // check if it's in use +#ifdef DEBUG + if (pRoot->mountRef == 1 && pRoot->handleRef > 0) { + fprintf(stderr, "Warning: File system to be unmounted still " + "has file descriptors open. The file system will not " + "be deallocated until these are all closed.\n"); + } +#endif + + // TODO: lock (and furtheron unlock) repository + + // remove from mount tree + uio_mountTreeRemoveMountInfo(mountHandle->repository, + mountHandle->mountInfo->mountTree, + mountHandle->mountInfo); + + // remove from mount list. + uio_repositoryRemoveMount(mountHandle->repository, + mountHandle->mountInfo); + + uio_MountInfo_delete(mountHandle->mountInfo); + + uio_MountHandle_delete(mountHandle); + uio_PRoot_unrefMount(pRoot); + return 0; +} + +int +uio_unmountAllDirs(uio_Repository *repository) { + int i; + + i = repository->numMounts; + while (i--) + uio_unmountDir(repository->mounts[i]->mountHandle); + return 0; +} + +uio_FileSystemID +uio_getMountFileSystemType(uio_MountHandle *mountHandle) { + return mountHandle->mountInfo->fsID; +} + +int +uio_close(uio_Handle *handle) { + uio_Handle_unref(handle); + return 0; +} + +int +uio_rename(uio_DirHandle *oldDir, const char *oldPath, + uio_DirHandle *newDir, const char *newPath) { + uio_PDirHandle *oldPReadDir, *newPReadDir, *newPWriteDir; + uio_MountInfo *oldReadMountInfo, *newReadMountInfo, *newWriteMountInfo; + char *oldName, *newName; + int retVal; + + if (uio_getPhysicalAccess(oldDir, oldPath, O_RDONLY, 0, + &oldReadMountInfo, &oldPReadDir, NULL, + NULL, NULL, NULL, &oldName) == -1) { + // errno is set + return -1; + } + + if (uio_getPhysicalAccess(newDir, newPath, O_WRONLY | O_CREAT | O_EXCL, + uio_GPA_NOWRITE, &newReadMountInfo, &newPReadDir, NULL, + &newWriteMountInfo, &newPWriteDir, NULL, &newName) == -1) { + int savedErrno = errno; + uio_PDirHandle_unref(oldPReadDir); + uio_free(oldName); + errno = savedErrno; + return -1; + } + + if (oldReadMountInfo != newWriteMountInfo) { + uio_PDirHandle_unref(oldPReadDir); + uio_PDirHandle_unref(newPReadDir); + uio_PDirHandle_unref(newPWriteDir); + uio_free(oldName); + uio_free(newName); + errno = EXDEV; + return -1; + } + + if (uio_mountInfoIsReadOnly(oldReadMountInfo)) { + // XXX: Doesn't uio_getPhysicalAccess already handle this? + // It doesn't return EROFS though; perhaps it should. + uio_PDirHandle_unref(oldPReadDir); + uio_PDirHandle_unref(newPReadDir); + uio_PDirHandle_unref(newPWriteDir); + uio_free(oldName); + uio_free(newName); + errno = EROFS; + return -1; + } + + if (oldReadMountInfo->pDirHandle->pRoot->handler->rename == NULL) { + uio_PDirHandle_unref(oldPReadDir); + uio_PDirHandle_unref(newPReadDir); + uio_PDirHandle_unref(newPWriteDir); + uio_free(oldName); + uio_free(newName); + errno = ENOSYS; + return -1; + } + retVal = (oldReadMountInfo->pDirHandle->pRoot->handler->rename)( + oldPReadDir, oldName, newPWriteDir, newName); + if (retVal == -1) { + int savedErrno = errno; + uio_PDirHandle_unref(oldPReadDir); + uio_PDirHandle_unref(newPReadDir); + uio_PDirHandle_unref(newPWriteDir); + uio_free(oldName); + uio_free(newName); + errno = savedErrno; + return -1; + } + + uio_PDirHandle_unref(oldPReadDir); + uio_PDirHandle_unref(newPReadDir); + uio_PDirHandle_unref(newPWriteDir); + uio_free(oldName); + uio_free(newName); + return 0; +} + +int +uio_access(uio_DirHandle *dir, const char *path, int mode) { + (void) dir; + (void) path; + (void) mode; + errno = ENOSYS; // Not implemented. + return -1; + +#if 0 + uio_PDirHandle *pReadDir; + uio_MountInfo *readMountInfo; + char *name; + int result; + + if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0, + &readMountInfo, &pReadDir, NULL, + NULL, NULL, NULL, &name) == -1) { + // XXX: I copied this part from uio_stat(). Is this what I need? + if (uio_accessDir(dir, path, statBuf) == -1) { + // errno is set + return -1; + } + return 0; + } + + if (pReadDir->pRoot->handler->access == NULL) { + uio_PDirHandle_unref(pReadDir); + uio_free(name); + errno = ENOSYS; + return -1; + } + + result = (pReadDir->pRoot->handler->access)(pReadDir, name, mode); + if (result == -1) { + int savedErrno = errno; + uio_PDirHandle_unref(pReadDir); + uio_free(name); + errno = savedErrno; + return -1; + } + + uio_PDirHandle_unref(pReadDir); + uio_free(name); + return result; +#endif +} + +#if 0 +// auxiliary function to uio_access +static int +uio_accessDir(uio_DirHandle *dirHandle, const char *path, int mode) { + int numPDirHandles; + uio_PDirHandle **pDirHandles; + + if (mode & R_OK) + { + // Read permission is always granted. Nothing to check here. + } + + if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path), + &pDirHandles, &numPDirHandles, NULL) == -1) { + // errno is set + return -1; + } + + if (numPDirHandles == 0) { + errno = ENOENT; + return -1; + } + + if (mode & F_OK) + { + // We need to check whether each of the directories is complete + + // WORK + } + + if (mode & W_OK) { + // If there is any directory where writing is allowed, then + // we can write. + + // WORK + errno = ENOENT; + return -1; + +#if 0 + if (uio_statOneDir(pDirHandles[0], statBuf) == -1) { + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = savedErrno; + return -1; + } + // TODO: atm, fstat'ing a dir will show the info for the topmost + // dir. Maybe it would make sense of merging the bits. (How?) + +#if 0 + for (i = 1; i < numPDirHandles; i++) { + struct stat statOne; + uio_PDirHandle *pDirHandle; + + if (statOneDir(pDirHandles[i], &statOne) == -1) { + // errno is set + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = savedErrno; + return -1; + } + + // Merge dirs: + + + } +#endif +#endif + } + + if (mode & X_OK) { + // XXX: Not implemented. + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = ENOSYS; + return -1; + } + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + return 0; +} +#endif + +int +uio_fstat(uio_Handle *handle, struct stat *statBuf) { + if (handle->root->handler->fstat == NULL) { + errno = ENOSYS; + return -1; + } + return (handle->root->handler->fstat)(handle, statBuf); +} + +int +uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf) { + uio_PDirHandle *pReadDir; + uio_MountInfo *readMountInfo; + char *name; + int result; + + if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0, + &readMountInfo, &pReadDir, NULL, + NULL, NULL, NULL, &name) == -1) { + if (uio_statDir(dir, path, statBuf) == -1) { + // errno is set + return -1; + } + return 0; + } + + if (pReadDir->pRoot->handler->stat == NULL) { + uio_PDirHandle_unref(pReadDir); + uio_free(name); + errno = ENOSYS; + return -1; + } + + result = (pReadDir->pRoot->handler->stat)(pReadDir, name, statBuf); + if (result == -1) { + int savedErrno = errno; + uio_PDirHandle_unref(pReadDir); + uio_free(name); + errno = savedErrno; + return -1; + } + + uio_PDirHandle_unref(pReadDir); + uio_free(name); + return result; +} + +// auxiliary function to uio_stat +static int +uio_statDir(uio_DirHandle *dirHandle, const char *path, + struct stat *statBuf) { + int numPDirHandles; + uio_PDirHandle **pDirHandles; + + if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path), + &pDirHandles, &numPDirHandles, NULL) == -1) { + // errno is set + return -1; + } + + if (numPDirHandles == 0) { + errno = ENOENT; + return -1; + } + + if (uio_statOneDir(pDirHandles[0], statBuf) == -1) { + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = savedErrno; + return -1; + } + // TODO: atm, fstat'ing a dir will show the info for the topmost + // dir. Maybe it would make sense of merging the bits. (How?) + +#if 0 + for (i = 1; i < numPDirHandles; i++) { + struct stat statOne; + uio_PDirHandle *pDirHandle; + + if (statOneDir(pDirHandles[i], &statOne) == -1) { + // errno is set + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = savedErrno; + return -1; + } + + // Merge dirs: + + + } +#endif + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + return 0; +} + +static int +uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf) { + if (pDirHandle->pRoot->handler->stat == NULL) { + errno = ENOSYS; + return -1; + } + return (pDirHandle->pRoot->handler->stat)(pDirHandle, ".", statBuf); + // sets errno on error +} + +int +uio_mkdir(uio_DirHandle *dir, const char *path, mode_t mode) { + uio_PDirHandle *pReadDir, *pWriteDir; + uio_MountInfo *readMountInfo, *writeMountInfo; + char *name; + uio_PDirHandle *newDirHandle; + + if (uio_getPhysicalAccess(dir, path, O_WRONLY | O_CREAT | O_EXCL, 0, + &readMountInfo, &pReadDir, NULL, + &writeMountInfo, &pWriteDir, NULL, &name) == -1) { + // errno is set + if (errno == EISDIR) + errno = EEXIST; + return -1; + } + uio_PDirHandle_unref(pReadDir); + + if (pWriteDir->pRoot->handler->mkdir == NULL) { + uio_free(name); + uio_PDirHandle_unref(pWriteDir); + errno = ENOSYS; + return -1; + } + + newDirHandle = (pWriteDir->pRoot->handler->mkdir)(pWriteDir, name, mode); + if (newDirHandle == NULL) { + int savedErrno = errno; + uio_free(name); + uio_PDirHandle_unref(pWriteDir); + errno = savedErrno; + return -1; + } + + uio_PDirHandle_unref(pWriteDir); + uio_PDirHandle_unref(newDirHandle); + uio_free(name); + return 0; +} + +uio_Handle * +uio_open(uio_DirHandle *dir, const char *path, int flags, mode_t mode) { + uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle; + uio_MountInfo *readMountInfo, *writeMountInfo; + char *name; + uio_Handle *handle; + + if (uio_getPhysicalAccess(dir, path, flags, 0, + &readMountInfo, &readPDirHandle, NULL, + &writeMountInfo, &writePDirHandle, NULL, &name) == -1) { + // errno is set + return NULL; + } + + if ((flags & O_ACCMODE) == O_RDONLY) { + // WritePDirHandle is not filled in. + pDirHandle = readPDirHandle; + } else if (readPDirHandle == writePDirHandle) { + // In general, the dirs can be the same even when the handles are + // not the same. But here it works, because uio_getPhysicalAccess + // guarantees it. + uio_PDirHandle_unref(writePDirHandle); + pDirHandle = readPDirHandle; + } else { + // need to write + uio_PDirEntryHandle *entry; + + entry = uio_getPDirEntryHandle(readPDirHandle, name); + if (entry != NULL) { + // file already exists + uio_PDirEntryHandle_unref(entry); + if ((flags & O_CREAT) == O_CREAT && + (flags & O_EXCL) == O_EXCL) { + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_PDirHandle_unref(writePDirHandle); + errno = EEXIST; + return NULL; + } + if ((flags & O_TRUNC) == O_TRUNC) { + // No use copying the file to the writable dir. + // As it doesn't exists there, O_TRUNC needs to be turned off + // though. + flags &= ~O_TRUNC; + } else { + // file needs to be copied + if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle, + name) == -1) { + int savedErrno = errno; + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_PDirHandle_unref(writePDirHandle); + errno = savedErrno; + return NULL; + } + } + } else { + // file does not exist + if (((flags & O_ACCMODE) == O_RDONLY) || + (flags & O_CREAT) != O_CREAT) { + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_PDirHandle_unref(writePDirHandle); + errno = ENOENT; + return NULL; + } + } + uio_PDirHandle_unref(readPDirHandle); + pDirHandle = writePDirHandle; + } + + handle = (pDirHandle->pRoot->handler->open)(pDirHandle, name, flags, mode); + // Also adds a new entry to the physical dir if appropriate. + if (handle == NULL) { + int savedErrno = errno; + uio_free(name); + uio_PDirHandle_unref(pDirHandle); + errno = savedErrno; + return NULL; + } + + uio_free(name); + uio_PDirHandle_unref(pDirHandle); + return handle; +} + +uio_DirHandle * +uio_openDir(uio_Repository *repository, const char *path, int flags) { + uio_DirHandle *dirHandle; + const char * const rootStr = ""; + + dirHandle = uio_DirHandle_new(repository, + unconst(rootStr), unconst(rootStr)); + // dirHandle->path will be replaced before uio_openDir() + // exits() + if (uio_verifyPath(dirHandle, path, &dirHandle->path) == -1) { + int savedErrno = errno; + uio_DirHandle_free(dirHandle); + errno = savedErrno; + return NULL; + } + // dirHandle->path is no longer equal to 'path' at this point. + // TODO: increase ref in repository? + dirHandle->rootEnd = dirHandle->path; + if (flags & uio_OD_ROOT) + dirHandle->rootEnd += strlen(dirHandle->path); + return dirHandle; +} + +uio_DirHandle * +uio_openDirRelative(uio_DirHandle *base, const char *path, int flags) { + uio_DirHandle *dirHandle; + char *newPath; + + if (uio_verifyPath(base, path, &newPath) == -1) { + // errno is set + return NULL; + } + if (flags & uio_OD_ROOT) { + dirHandle = uio_DirHandle_new(base->repository, + newPath, newPath + strlen(newPath)); + // TODO: increase ref in base->repository? + } else { + // use the root of the base dir + dirHandle = uio_DirHandle_new(base->repository, + newPath, newPath + (base->rootEnd - base->path)); + } + return dirHandle; +} + +int +uio_closeDir(uio_DirHandle *dirHandle) { + uio_DirHandle_unref(dirHandle); + return 0; +} + +ssize_t +uio_read(uio_Handle *handle, void *buf, size_t count) { + return (handle->root->handler->read)(handle, buf, count); +} + +int +uio_rmdir(uio_DirHandle *dirHandle, const char *path) { + int numPDirHandles; + uio_PDirHandle *pDirHandle, **pDirHandles; + const char *pathEnd, *name; + uio_PDirEntryHandle *entry; + uio_MountTreeItem **items; + int i; + int numDeleted; + + pathEnd = strrchr(path, '/'); + if (pathEnd == NULL) { + pathEnd = path; + name = path; + } else + name = pathEnd + 1; + + if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path, + &pDirHandles, &numPDirHandles, &items) == -1) { + // errno is set + return -1; + } + + entry = NULL; + // Should be set before a possible goto. + + if (name[0] == '\0') { + // path was of the form "foo/bar/" or "/foo/bar/" + // These are intentionally not accepted. + // I see this as a path and not as a directory identifier. + errno = ENOENT; + goto err; + } + + numDeleted = 0; + for (i = 0; i < numPDirHandles; i++) { + pDirHandle = pDirHandles[i]; + entry = uio_getPDirEntryHandle(pDirHandle, name); + + if (entry == NULL) + continue; + + if (!uio_PDirEntryHandle_isDir(entry)) { + errno = ENOTDIR; + goto err; + } + + if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) { + errno = EROFS; + goto err; + } + + if (pDirHandle->pRoot->handler->rmdir == NULL) { + errno = ENOSYS; + goto err; + } + + if ((pDirHandle->pRoot->handler->rmdir)(pDirHandle, name) == -1) { + // errno is set + goto err; + } + numDeleted++; + uio_PDirEntryHandle_unref(entry); + } + entry = NULL; + + if (numDeleted == 0) { + errno = ENOENT; + goto err; + } + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + uio_free(items); + return 0; + +err: + { + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + uio_free(items); + if (entry != NULL) + uio_PDirEntryHandle_unref(entry); + errno = savedErrno; + return -1; + } +} + +static void +uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[], int numPDirHandles) { + while (numPDirHandles--) + uio_PDirHandle_unref(pDirHandles[numPDirHandles]); + uio_free(pDirHandles); +} + +int +uio_lseek(uio_Handle *handle, off_t offset, int whence) { + if (handle->root->handler->seek == NULL) { + errno = ENOSYS; + return -1; + } + return (handle->root->handler->seek)(handle, offset, whence); +} + +ssize_t +uio_write(uio_Handle *handle, const void *buf, size_t count) { + if (handle->root->handler->write == NULL) { + errno = ENOSYS; + return -1; + } + return (handle->root->handler->write)(handle, buf, count); +} + +int +uio_unlink(uio_DirHandle *dirHandle, const char *path) { + int numPDirHandles; + uio_PDirHandle *pDirHandle, **pDirHandles; + const char *pathEnd, *name; + uio_PDirEntryHandle *entry; + uio_MountTreeItem **items; + int i; + int numDeleted; + + pathEnd = strrchr(path, '/'); + if (pathEnd == NULL) { + pathEnd = path; + name = path; + } else + name = pathEnd + 1; + + if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path, + &pDirHandles, &numPDirHandles, &items) == -1) { + // errno is set + return -1; + } + + entry = NULL; + // Should be set before a possible goto. + + if (name[0] == '\0') { + // path was of the form "foo/bar/" or "/foo/bar/" + errno = ENOENT; + goto err; + } + + numDeleted = 0; + for (i = 0; i < numPDirHandles; i++) { + pDirHandle = pDirHandles[i]; + entry = uio_getPDirEntryHandle(pDirHandle, name); + + if (entry == NULL) + continue; + + if (uio_PDirEntryHandle_isDir(entry)) { + errno = EISDIR; + goto err; + } + + if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) { + errno = EROFS; + goto err; + } + + if (pDirHandle->pRoot->handler->unlink == NULL) { + errno = ENOSYS; + goto err; + } + + if ((pDirHandle->pRoot->handler->unlink)(pDirHandle, name) == -1) { + // errno is set + goto err; + } + numDeleted++; + uio_PDirEntryHandle_unref(entry); + } + entry = NULL; + + if (numDeleted == 0) { + errno = ENOENT; + goto err; + } + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + uio_free(items); + return 0; + +err: + { + int savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + uio_free(items); + if (entry != NULL) + uio_PDirEntryHandle_unref(entry); + errno = savedErrno; + return -1; + } +} + +// inPath and *outPath may point to the same location +int +uio_getFileLocation(uio_DirHandle *dir, const char *inPath, + int flags, uio_MountHandle **mountHandle, char **outPath) { + uio_PDirHandle *readPDirHandle, *writePDirHandle; + uio_MountInfo *readMountInfo, *writeMountInfo, *mountInfo; + char *name; + char *readPRootPath, *writePRootPath, *pRootPath; + + if (uio_getPhysicalAccess(dir, inPath, flags, 0, + &readMountInfo, &readPDirHandle, &readPRootPath, + &writeMountInfo, &writePDirHandle, &writePRootPath, + &name) == -1) { + // errno is set + return -1; + } + + // TODO: This code is partly the same as the code in uio_open(). + // probably some code could be put in a seperate function. + if ((flags & O_ACCMODE) == O_RDONLY) { + // WritePDirHandle is not filled in. + uio_PDirHandle_unref(readPDirHandle); + pRootPath = readPRootPath; + mountInfo = readMountInfo; + } else if (readPDirHandle == writePDirHandle) { + // In general, the dirs can be the same even when the handles are + // not the same. But here it works, because uio_getPhysicalAccess + // guarantees it. + uio_PDirHandle_unref(readPDirHandle); + uio_PDirHandle_unref(writePDirHandle); + pRootPath = readPRootPath; + mountInfo = readMountInfo; + uio_free(writePRootPath); + } else { + // need to write + uio_PDirEntryHandle *entry; + + entry = uio_getPDirEntryHandle(readPDirHandle, name); + if (entry != NULL) { + // file already exists + uio_PDirEntryHandle_unref(entry); + if ((flags & O_CREAT) == O_CREAT && + (flags & O_EXCL) == O_EXCL) { + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_free(readPRootPath); + uio_PDirHandle_unref(writePDirHandle); + uio_free(writePRootPath); + errno = EEXIST; + return -1; + } + if ((flags & O_TRUNC) == O_TRUNC) { + // No use copying the file to the writable dir. + // As it doesn't exists there, O_TRUNC needs to be turned off + // though. + flags &= ~O_TRUNC; + } else { + // file needs to be copied + if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle, + name) == -1) { + int savedErrno = errno; + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_free(readPRootPath); + uio_PDirHandle_unref(writePDirHandle); + uio_free(writePRootPath); + errno = savedErrno; + return -1; + } + } + } else { + // file does not exist + if (((flags & O_ACCMODE) == O_RDONLY) || + (flags & O_CREAT) != O_CREAT) { + uio_free(name); + uio_PDirHandle_unref(readPDirHandle); + uio_free(readPRootPath); + uio_PDirHandle_unref(writePDirHandle); + uio_free(writePRootPath); + errno = ENOENT; + return -1; + } + } + uio_PDirHandle_unref(readPDirHandle); + uio_PDirHandle_unref(writePDirHandle); + pRootPath = writePRootPath; + mountInfo = writeMountInfo; + uio_free(readPRootPath); + } + + uio_free(name); + + *mountHandle = mountInfo->mountHandle; + *outPath = pRootPath; + return 0; +} + + +// *** begin dirList stuff *** // + +#define uio_DIR_BUFFER_SIZE 2048 + // Large values will give significantly better speed for large + // directories. What is stored in the buffer is file names + // plus one pointer per file name, so with an average size of 16 + // characters per file (including \0), a buffer of size 2048 will + // store approximately 100 files. + // It should be at least big enough to store one entry (NAME_MAX is + // 255 on POSIX systems). + // TODO: add a compile-time check for this + +typedef struct uio_DirBufferLink { + char *buffer; + int numEntries; + struct uio_DirBufferLink *next; +} uio_DirBufferLink; + +static int strPtrCmp(const char * const *ptr1, const char * const *ptr2); +static void uio_DirBufferLink_free(uio_DirBufferLink *sdbl); +static void uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink); +static uio_DirList *uio_getDirListMulti(uio_PDirHandle **pDirHandles, + int numPDirHandles, const char *pattern, match_MatchType matchType); +static uio_DirList *uio_makeDirList(const char **newNames, + const char * const *names, int numNames); +static uio_DirList *uio_DirList_new(const char **names, int numNames, + char *buffer); +static void uio_collectDirEntries(uio_PDirHandle *pDirHandle, + uio_DirBufferLink **linkPtr, int *numEntries); +static inline uio_DirList *uio_DirList_alloc(void); +static void uio_filterNames(const char * const *names, int numNames, + const char **newNames, int *numNewNames, + match_MatchContext *matchContext); + +static uio_EntriesContext *uio_openEntriesPhysical(uio_PDirHandle *dirHandle); +static int uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf, + size_t len); +static void uio_closeEntriesPhysical(uio_EntriesContext *iterator); +static uio_EntriesContext *uio_EntriesContext_new(uio_PRoot *pRoot, + uio_NativeEntriesContext *native); +static inline uio_EntriesContext *uio_EntriesContext_alloc(void); +static inline void uio_EntriesContext_delete(uio_EntriesContext *entriesContext); +static inline void uio_EntriesContext_free(uio_EntriesContext + *entriesContext); + +// The caller may modify the elements of the .names field of the result, but +// .names itself, and the rest of the elements of dirList should be left +// alone, so that they will be freed by uio_DirList_free(). +uio_DirList * +uio_getDirList(uio_DirHandle *dirHandle, const char *path, const char *pattern, + match_MatchType matchType) { + int numPDirHandles; + uio_PDirHandle **pDirHandles; + uio_DirList *result; + + if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path), + &pDirHandles, &numPDirHandles, NULL) == -1) { + // errno is set + return NULL; + } + + if (numPDirHandles == 0) { + assert(pDirHandles == NULL); + // nothing to free + return uio_DirList_new(NULL, 0, NULL); + } + + result = uio_getDirListMulti(pDirHandles, numPDirHandles, pattern, + matchType); + + { + int savedErrno; + savedErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = savedErrno; + } + return result; +} + +// Names and newNames may point to the same location. +// numNewNames may point to &numNames. +static void +uio_filterDoubleNames(const char * const *names, int numNames, + const char **newNames, int *numNewNames) { + const char * const *endNames; + const char *prevName; + int newNum; + + if (numNames == 0) { + *numNewNames = 0; + return; + } + + endNames = names + numNames; + prevName = *names; + *newNames = *names; + newNames++; + names++; + newNum = 1; + while (names < endNames) { + if (strcmp(prevName, *names) != 0) { + *newNames = *names; + newNum++; + prevName = *names; + newNames++; + } + names++; + } + *numNewNames = newNum; +} + +static uio_DirList * +uio_getDirListMulti(uio_PDirHandle **pDirHandles, + int numPDirHandles, const char *pattern, match_MatchType matchType) { + int pDirI; // physical dir iterator + uio_DirBufferLink **links; // array of bufferLinks for each physical dir + uio_DirBufferLink *linkPtr; + int *numNames; // number of entries in each physical dir + int totalNumNames; + const char **bigNameBuffer; // buffer where all names will end up together + const char **destPtr; + uio_DirList *result; + match_Result matchResult; + match_MatchContext *matchContext; + + matchResult = match_prepareContext(pattern, &matchContext, matchType); + if (matchResult != match_OK) { +#ifdef DEBUG + fprintf(stderr, "Error compiling match function: %s.\n", + match_errorString(matchContext, matchResult)); +#endif + match_freeContext(matchContext); + errno = EIO; + // I actually want to signal an internal error. + // EIO comes closes + return NULL; + } + + // first get the directory listings for all seperate relevant dirs. + totalNumNames = 0; + links = uio_malloc(numPDirHandles * sizeof (uio_DirBufferLink *)); + numNames = uio_malloc(numPDirHandles * sizeof (int)); + for (pDirI = 0; pDirI < numPDirHandles; pDirI++) { + uio_collectDirEntries(pDirHandles[pDirI], &links[pDirI], + &numNames[pDirI]); + totalNumNames += numNames[pDirI]; + } + + bigNameBuffer = uio_malloc(totalNumNames * sizeof (uio_DirBufferLink *)); + + // Fill the bigNameBuffer with all the names from all the DirBufferLinks + // of all the physical dirs. + destPtr = bigNameBuffer; + totalNumNames = 0; + for (pDirI = 0; pDirI < numPDirHandles; pDirI++) { + for (linkPtr = links[pDirI]; linkPtr != NULL; + linkPtr = linkPtr->next) { + int numNewNames; + uio_filterNames((const char * const *) linkPtr->buffer, + linkPtr->numEntries, destPtr, + &numNewNames, matchContext); + totalNumNames += numNewNames; + destPtr += numNewNames; + } + } + + match_freeContext(matchContext); + + // Sort the bigNameBuffer + // Necessary for removing doubles. + // Not really necessary if the big list was the result of only one + // physical dir, but let's output a sorted list anyhow. + qsort((void *) bigNameBuffer, totalNumNames, sizeof (char *), + (int (*)(const void *, const void *)) strPtrCmp); + + // remove doubles + // (unnecessary if the big list was the result of only one physical dir) + if (numPDirHandles > 1) { + uio_filterDoubleNames(bigNameBuffer, totalNumNames, + bigNameBuffer, &totalNumNames); + } + + // resize the bigNameBuffer + bigNameBuffer = uio_realloc((void *) bigNameBuffer, + totalNumNames * sizeof (char *)); + + // put the lot in a DirList, copying the strings themselves + result = uio_makeDirList(bigNameBuffer, bigNameBuffer, + totalNumNames); + + // free the old junk + for (pDirI = 0; pDirI < numPDirHandles; pDirI++) + uio_DirBufferChain_free(links[pDirI]); + uio_free(links); + uio_free(numNames); + + return result; +} + +// 'buffer' and 'names' may be the same dir +// 'names' contains an array of 'numNames' pointers. +// 'newNames', if non-NULL, will be used as the array of new pointers +// (to a copy of the strings) in the DirList. +static uio_DirList * +uio_makeDirList(const char **newNames, const char * const *names, + int numNames) { + int i; + size_t len, totLen; + char *bufPtr; + uio_DirList *result; + + if (newNames == NULL) + newNames = uio_malloc(numNames * sizeof (char *)); + + totLen = 0; + for (i = 0; i < numNames; i++) + totLen += strlen(names[i]); + totLen += numNames; + // for the \0's + + result = uio_DirList_new(newNames, numNames, uio_malloc(totLen)); + + bufPtr = result->buffer; + for (i = 0; i < numNames; i++) { + len = strlen(names[i]) + 1; + memcpy(bufPtr, names[i], len); + newNames[i] = bufPtr; + bufPtr += len; + } + return result; +} + +static void +uio_collectDirEntries(uio_PDirHandle *pDirHandle, uio_DirBufferLink **linkPtr, + int *numEntries) { + uio_EntriesContext *entriesContext; + uio_DirBufferLink **linkEndPtr; // where to attach the next link + int numRead; + int totalEntries; + char *buffer; + + entriesContext = uio_openEntriesPhysical(pDirHandle); + if (entriesContext == NULL) { +#ifdef DEBUG + fprintf(stderr, "Error: uio_openEntriesPhysical() failed: %s\n", + strerror(errno)); +#endif + *linkPtr = NULL; + *numEntries = 0; + return; + } + linkEndPtr = linkPtr; + totalEntries = 0; + while (1) { + *linkEndPtr = uio_malloc(sizeof (uio_DirBufferLink)); + buffer = uio_malloc(uio_DIR_BUFFER_SIZE); + (*linkEndPtr)->buffer = buffer; + numRead = uio_readEntriesPhysical(entriesContext, buffer, + uio_DIR_BUFFER_SIZE); + if (numRead == 0) { + fprintf(stderr, "Warning: uio_DIR_BUFFER_SIZE is too small to " + "hold a certain large entry on its own!\n"); + uio_DirBufferLink_free(*linkEndPtr); + break; + } + totalEntries += numRead; + (*linkEndPtr)->numEntries = numRead; + if (((char **) buffer)[numRead - 1] == NULL) { + // The entry being NULL means this is the last buffer + // Decrement the amount of queries to get the real number. + (*linkEndPtr)->numEntries--; + totalEntries--; + linkEndPtr = &(*linkEndPtr)->next; + break; + } + linkEndPtr = &(*linkEndPtr)->next; + } + *linkEndPtr = NULL; + uio_closeEntriesPhysical(entriesContext); + *numEntries = totalEntries; +} + +static void +uio_filterNames(const char * const *names, int numNames, + const char **newNames, int *numNewNames, + match_MatchContext *matchContext) { + int newNum; + const char * const *namesEnd; + match_Result matchResult; + + newNum = 0; + namesEnd = names + numNames; + while (names < namesEnd) { + matchResult = match_matchPattern(matchContext, *names); + if (matchResult == match_MATCH) { + *newNames = *names; + newNames++; + newNum++; + } else if (matchResult != match_NOMATCH) { + fprintf(stderr, "Error trying to match pattern: %s.\n", + match_errorString(matchContext, matchResult)); + } + names++; + } + *numNewNames = newNum; +} + +static int +strPtrCmp(const char * const *ptr1, const char * const *ptr2) { + return strcmp(*ptr1, *ptr2); +} + +static uio_EntriesContext * +uio_openEntriesPhysical(uio_PDirHandle *dirHandle) { + uio_NativeEntriesContext *native; + uio_PRoot *pRoot; + + pRoot = dirHandle->pRoot; + + assert(pRoot->handler->openEntries != NULL); + native = pRoot->handler->openEntries(dirHandle); + if (native == NULL) + return NULL; + uio_PRoot_refHandle(pRoot); + return uio_EntriesContext_new(pRoot, native); +} + +static int +uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf, size_t len) { + assert(iterator->pRoot->handler->readEntries != NULL); + return iterator->pRoot->handler->readEntries(&iterator->native, buf, len); +} + +static void +uio_closeEntriesPhysical(uio_EntriesContext *iterator) { + assert(iterator->pRoot->handler->closeEntries != NULL); + iterator->pRoot->handler->closeEntries(iterator->native); + uio_PRoot_unrefHandle(iterator->pRoot); + uio_EntriesContext_delete(iterator); +} + +static uio_EntriesContext * +uio_EntriesContext_new(uio_PRoot *pRoot, uio_NativeEntriesContext *native) { + uio_EntriesContext *result; + result = uio_EntriesContext_alloc(); + result->pRoot = pRoot; + result->native = native; + return result; +} + +static inline uio_EntriesContext * +uio_EntriesContext_alloc(void) { + return uio_malloc(sizeof (uio_EntriesContext)); +} + +static inline void +uio_EntriesContext_delete(uio_EntriesContext *entriesContext) { + uio_EntriesContext_free(entriesContext); +} + +static inline void +uio_EntriesContext_free(uio_EntriesContext *entriesContext) { + uio_free(entriesContext); +} + +static void +uio_DirBufferLink_free(uio_DirBufferLink *dirBufferLink) { + uio_free(dirBufferLink->buffer); + uio_free(dirBufferLink); +} + +static void +uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink) { + uio_DirBufferLink *next; + + while (dirBufferLink != NULL) { + next = dirBufferLink->next; + uio_DirBufferLink_free(dirBufferLink); + dirBufferLink = next; + } +} + +static uio_DirList * +uio_DirList_new(const char **names, int numNames, char *buffer) { + uio_DirList *result; + + result = uio_DirList_alloc(); + result->names = names; + result->numNames = numNames; + result->buffer = buffer; + return result; +} + +static uio_DirList * +uio_DirList_alloc(void) { + return uio_malloc(sizeof (uio_DirList)); +} + +void +uio_DirList_free(uio_DirList *dirList) { + if (dirList->buffer) + uio_free(dirList->buffer); + if (dirList->names) + uio_free((void *) dirList->names); + uio_free(dirList); +} + +// *** end DirList stuff *** // + + +// *** PDirEntryHandle stuff *** // + +uio_PDirEntryHandle * +uio_getPDirEntryHandle(const uio_PDirHandle *dirHandle, + const char *name) { + assert(dirHandle->pRoot->handler != NULL); + return dirHandle->pRoot->handler->getPDirEntryHandle(dirHandle, name); +} + +void +uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle) { + if (pDirHandle->extra == NULL) + return; + assert(pDirHandle->pRoot->handler->deletePDirHandleExtra != NULL); + pDirHandle->pRoot->handler->deletePDirHandleExtra(pDirHandle->extra); +} + +void +uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle) { + if (pFileHandle->extra == NULL) + return; + assert(pFileHandle->pRoot->handler->deletePFileHandleExtra != NULL); + pFileHandle->pRoot->handler->deletePFileHandleExtra(pFileHandle->extra); +} + +uio_PDirHandle * +uio_PDirHandle_new(uio_PRoot *pRoot, uio_PDirHandleExtra extra) { + uio_PDirHandle *result; + + result = uio_PDirHandle_alloc(); + result->flags = uio_PDirEntryHandle_TYPE_DIR; + result->ref = 1; + result->pRoot = pRoot; + result->extra = extra; + return result; +} + +void +uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle) { + if (uio_PDirEntryHandle_isDir(pDirEntryHandle)) { + uio_PDirHandle_delete((uio_PDirHandle *) pDirEntryHandle); + } else { + uio_PFileHandle_delete((uio_PFileHandle *) pDirEntryHandle); + } +} + +void +uio_PDirHandle_delete(uio_PDirHandle *pDirHandle) { + uio_PDirHandle_deletePDirHandleExtra(pDirHandle); + uio_PDirHandle_free(pDirHandle); +} + +static inline uio_PDirHandle * +uio_PDirHandle_alloc(void) { + uio_PDirHandle *result = uio_malloc(sizeof (uio_PDirHandle)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_PDirHandle, (void *) result); +#endif + return result; +} + +static inline void +uio_PDirHandle_free(uio_PDirHandle *pDirHandle) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_PDirHandle, (void *) pDirHandle); +#endif + uio_free(pDirHandle); +} + +uio_PFileHandle * +uio_PFileHandle_new(uio_PRoot *pRoot, uio_PFileHandleExtra extra) { + uio_PFileHandle *result; + + result = uio_PFileHandle_alloc(); + result->flags = uio_PDirEntryHandle_TYPE_REG; + result->ref = 1; + result->pRoot = pRoot; + result->extra = extra; + return result; +} + +void +uio_PFileHandle_delete(uio_PFileHandle *pFileHandle) { + uio_PFileHandle_deletePFileHandleExtra(pFileHandle); + uio_PFileHandle_free(pFileHandle); +} + +static inline uio_PFileHandle * +uio_PFileHandle_alloc(void) { + uio_PFileHandle *result = uio_malloc(sizeof (uio_PFileHandle)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_PFileHandle, (void *) result); +#endif + return result; +} + +static inline void +uio_PFileHandle_free(uio_PFileHandle *pFileHandle) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_PFileHandle, (void *) pFileHandle); +#endif + uio_free(pFileHandle); +} + +// *** PDirEntryHandle stuff *** // + + +// ref count set to 1 +uio_Handle * +uio_Handle_new(uio_PRoot *root, uio_NativeHandle native, int openFlags) { + uio_Handle *handle; + + handle = uio_Handle_alloc(); + handle->ref = 1; + uio_PRoot_refHandle(root); + handle->root = root; + handle->native = native; + handle->openFlags = openFlags; + return handle; +} + +void +uio_Handle_delete(uio_Handle *handle) { + (handle->root->handler->close)(handle); + uio_PRoot_unrefHandle(handle->root); + uio_Handle_free(handle); +} + +static inline uio_Handle * +uio_Handle_alloc(void) { + uio_Handle *result = uio_malloc(sizeof (uio_Handle)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_Handle, (void *) result); +#endif + return result; +} + +static inline void +uio_Handle_free(uio_Handle *handle) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_Handle, (void *) handle); +#endif + uio_free(handle); +} + +// ref count set to 1 +static uio_DirHandle * +uio_DirHandle_new(uio_Repository *repository, char *path, char *rootEnd) { + uio_DirHandle *result; + + result = uio_DirHandle_alloc(); + result->ref = 1; + result->repository = repository; + result->path = path; + result->rootEnd = rootEnd; + return result; +} + +void +uio_DirHandle_delete(uio_DirHandle *dirHandle) { + uio_free(dirHandle->path); + uio_DirHandle_free(dirHandle); +} + +static inline uio_DirHandle * +uio_DirHandle_alloc(void) { + uio_DirHandle *result = uio_malloc(sizeof (uio_DirHandle)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_DirHandle, (void *) result); +#endif + return result; +} + +static inline void +uio_DirHandle_free(uio_DirHandle *dirHandle) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_DirHandle, (void *) dirHandle); +#endif + uio_free(dirHandle); +} + +void +uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out) { + fprintf(out, "["); + fwrite(dirHandle->path, dirHandle->path - dirHandle->rootEnd, 1, out); + fprintf(out, "]%s", dirHandle->rootEnd); +} + +static uio_MountHandle * +uio_MountHandle_new(uio_Repository *repository, uio_MountInfo *mountInfo) { + uio_MountHandle *result; + + result = uio_MountHandle_alloc(); + result->repository = repository; + result->mountInfo = mountInfo; + return result; +} + +static inline void +uio_MountHandle_delete(uio_MountHandle *mountHandle) { + uio_MountHandle_free(mountHandle); +} + +static inline uio_MountHandle * +uio_MountHandle_alloc(void) { + uio_MountHandle *result = uio_malloc(sizeof (uio_MountHandle)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_MountHandle, (void *) result); +#endif + return result; +} + +static inline void +uio_MountHandle_free(uio_MountHandle *mountHandle) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_MountHandle, (void *) mountHandle); +#endif + uio_free(mountHandle); +} + + + diff --git a/src/libs/uio/io.h b/src/libs/uio/io.h new file mode 100644 index 0000000..813a2c3 --- /dev/null +++ b/src/libs/uio/io.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_IO_H_ +#define LIBS_UIO_IO_H_ + +#include <assert.h> +#include <sys/stat.h> + +typedef struct uio_Handle uio_Handle; +typedef struct uio_DirHandle uio_DirHandle; +typedef struct uio_DirList uio_DirList; +typedef struct uio_MountHandle uio_MountHandle; + +typedef enum { + uio_MOUNT_BOTTOM = (0 << 2), + uio_MOUNT_TOP = (1 << 2), + uio_MOUNT_BELOW = (2 << 2), + uio_MOUNT_ABOVE = (3 << 2) +} uio_MountLocation; + +#include "match.h" +#include "fstypes.h" +#include "mount.h" +#include "mounttree.h" +#include "uiostream.h" +#include "getint.h" +#include "debug.h" + +struct uio_AutoMount { + const char *pattern; + match_MatchType matchType; + uio_FileSystemID fileSystemID; + int mountFlags; +// uio_AutoMount **autoMount; + // automount rules to apply to file systems automounted + // because of this automount rule. +}; + +#ifndef uio_INTERNAL +struct uio_DirList { + const char **names; + int numNames; + // The rest of the fields are not visible from the outside +}; +#endif + + +// Initialise the resource system +void uio_init(void); + +// Uninitialise the resource system +void uio_unInit(void); + +// Open a repository. +uio_Repository *uio_openRepository(int flags); + +// Close a repository opened by uio_openRepository(). +void uio_closeRepository(uio_Repository *repository); + +// Mount a directory into a repository +uio_MountHandle *uio_mountDir(uio_Repository *destRep, const char *mountPoint, + uio_FileSystemID fsType, + uio_DirHandle *sourceDir, const char *sourcePath, + const char *inPath, uio_AutoMount **autoMount, int flags, + uio_MountHandle *relative); + +// Mount a repository directory into same repository at a different +// location. +// From fossil. +uio_MountHandle *uio_transplantDir(const char *mountPoint, + uio_DirHandle *sourceDir, int flags, uio_MountHandle *relative); + +// Unmount a previously mounted dir. +int uio_unmountDir(uio_MountHandle *mountHandle); + +// Unmount all previously mounted dirs. +int uio_unmountAllDirs(uio_Repository *repository); + +// Get the filesystem identifier for a mounted directory. +uio_FileSystemID uio_getMountFileSystemType(uio_MountHandle *mountHandle); + +// Open a file +uio_Handle *uio_open(uio_DirHandle *dir, const char *file, int flags, + mode_t mode); + +// Close a file descriptor for a file opened by uio_open +int uio_close(uio_Handle *handle); + +// Rename or move a file or directory. +int uio_rename(uio_DirHandle *oldDir, const char *oldPath, + uio_DirHandle *newDir, const char *newPath); + +// Test permissions on a file or directory. +int uio_access(uio_DirHandle *dir, const char *path, int mode); + +// Fstat a file descriptor +int uio_fstat(uio_Handle *handle, struct stat *statBuf); + +int uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf); + +int uio_mkdir(uio_DirHandle *dir, const char *name, mode_t mode); + +ssize_t uio_read(uio_Handle *handle, void *buf, size_t count); + +int uio_rmdir(uio_DirHandle *dirHandle, const char *path); + +int uio_lseek(uio_Handle *handle, off_t offset, int whence); + +ssize_t uio_write(uio_Handle *handle, const void *buf, size_t count); + +int uio_unlink(uio_DirHandle *dirHandle, const char *path); + +int uio_getFileLocation(uio_DirHandle *dir, const char *inPath, + int flags, uio_MountHandle **mountHandle, char **outPath); + +// Get a directory handle. +uio_DirHandle *uio_openDir(uio_Repository *repository, const char *path, + int flags); +#define uio_OD_ROOT 1 + +// Get a directory handle using a path relative to another handle. +uio_DirHandle *uio_openDirRelative(uio_DirHandle *base, const char *path, + int flags); + +// Release a directory handle +int uio_closeDir(uio_DirHandle *dirHandle); + +uio_DirList *uio_getDirList(uio_DirHandle *dirHandle, const char *path, + const char *pattern, match_MatchType matchType); +void uio_DirList_free(uio_DirList *dirList); + +// For debugging purposes +void uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out); + + +#ifdef DEBUG +# define uio_DEBUG +#endif + +#endif /* LIBS_UIO_IO_H_ */ + diff --git a/src/libs/uio/ioaux.c b/src/libs/uio/ioaux.c new file mode 100644 index 0000000..3dc0880 --- /dev/null +++ b/src/libs/uio/ioaux.c @@ -0,0 +1,930 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +// auxiliary functions for use by io.c + +#include <errno.h> +#include <fcntl.h> + +#include "ioaux.h" +#include "iointrn.h" +#include "uioport.h" +#include "paths.h" + +static int copyError(int error, + uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle, + uio_FileSystemHandler *toHandler, uio_Handle *toHandle, + uio_PDirHandle *toDir, const char *toName, char *buf); + +/** + * Follow a path starting from a specified physical dir for as long as + * possible. + * + * @param[in] startPDirHandle The physical dir to start from. + * @param[in] path The path to follow, relative to + * 'startPDirHandle'. + * @param[in] pathLen The string length of 'path'. + * @param[out] endPDirHandle The physical dir where you end up after + * following 'path' for as long as possible. Unmodified if an error + * occurs. + * @param[out] pathRest '*pathRest' will point into 'path' to the + * start the part that was not matched. Unmodified if an error occurs. + * + * @retval 0 if the complete path was matched + * @retval ENOENT if some component (the next one in '*pathRest') didn't + * exist. + * @retval ENODIR if a component (the next one in '*pathRest') did exist, + * but wasn't a dir. + * + * @note It is allowed to have 'endPDirHandle' point to pDirHandle, but + * care should be taken to keep a reference to the original so its + * reference counter can be decremented. + * @note It is allowed to have 'pathRest' point to 'path'. + */ +int +uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path, + size_t pathLen, uio_PDirHandle **endPDirHandle, + const char **pathRest) { + const char *pathEnd; + const char *partStart, *partEnd; + char *tempBuf; + uio_PDirHandle *pDirHandle; + uio_PDirEntryHandle *entry; + int retVal; + + uio_PDirHandle_ref(startPDirHandle); + pDirHandle = startPDirHandle; + tempBuf = uio_malloc(strlen(path) + 1); + // XXX: Use a dynamically allocated array when moving to C99. + pathEnd = path + pathLen; + getFirstPathComponent(path, pathEnd, &partStart, &partEnd); + for (;;) { + if (partStart == pathEnd) { + retVal = 0; + break; + } + memcpy(tempBuf, partStart, partEnd - partStart); + tempBuf[partEnd - partStart] = '\0'; + + entry = uio_getPDirEntryHandle(pDirHandle, tempBuf); + if (entry == NULL) { + retVal = ENOENT; + break; + } + if (!uio_PDirEntryHandle_isDir(entry)) { + uio_PDirEntryHandle_unref(entry); + retVal = ENOTDIR; + break; + } + uio_PDirHandle_unref(pDirHandle); + pDirHandle = (uio_PDirHandle *) entry; + getNextPathComponent(pathEnd, &partStart, &partEnd); + } + + uio_free(tempBuf); + *pathRest = partStart; + *endPDirHandle = pDirHandle; + return retVal; +} + +/** + * Create a directory inside a physical directory. All non-existant + * parent directories will be created as well. + * + * @param[in] pDirHandle The physical directory to which 'path' is relative + * @param[in] path The path to the directory to create, relative to + * 'pDirHandle' + * @param[in] pathLen The string length of 'path'. + * @param[in] mode The access mode for the newly created directories. + * + * @returns the new (physical) directory, or NULL if an error occurs, in + * which case #errno will be set. + */ +uio_PDirHandle * +uio_makePath(uio_PDirHandle *pDirHandle, const char *path, size_t pathLen, + mode_t mode) { + const char *rest, *start, *end; + uio_PDirHandle *(*mkdirFun)(uio_PDirHandle *, const char *, mode_t); + char *buf; + const char *pathEnd; + uio_PDirHandle *newPDirHandle; + + mkdirFun = pDirHandle->pRoot->handler->mkdir; + if (mkdirFun == NULL) { + errno = ENOSYS; + return NULL; + } + + pathEnd = path + pathLen; + + buf = uio_malloc(pathLen + 1); + // worst case length + // XXX: Use a dynamically allocated array when moving to C99. + + uio_walkPhysicalPath(pDirHandle, path, pathLen, &pDirHandle, &rest); + // The reference to the original pDirHandle is still kept + // by the calling function; uio_PDirHandle_unref should not + // be called for it. + // Rest now points into 'path' to the part from where no physical + // dir exists. + + getFirstPathComponent(rest, pathEnd, &start, &end); + while (start < pathEnd) { + memcpy(buf, start, end - start); + buf[end - start] = '\0'; + + newPDirHandle = mkdirFun(pDirHandle, buf, mode); + if (newPDirHandle == NULL) { + int savedErrno = errno; + uio_PDirHandle_unref(pDirHandle); + errno = savedErrno; + uio_free(buf); + return NULL; + } + uio_PDirHandle_unref(pDirHandle); + pDirHandle = newPDirHandle; + getNextPathComponent(pathEnd, &start, &end); + } + uio_free(buf); + return pDirHandle; +} + + +/** + * Copy a file from one physical directory to another. + * The copy will have the same file permissions as the original. + * + * @param[in] fromDir The physical directory where the file to copy is + * located. + * @param[in] fromName The name of the file to copy. + * @param[in] toDir The physical directory where to put the copy. + * @param[in] toName The name to use for the copy. + * + * @note It is up to the caller to make any relevant permissions checks. + * + * @note This function will fail if a file with the name in 'toName' already + * exists, leaving the original file intact. If an error occurs during + * copying, an attempt is made to remove the file that was to be the + * copy. + */ +int +uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName, + uio_PDirHandle *toDir, const char *toName) { + uio_FileSystemHandler *fromHandler, *toHandler; + uio_Handle *fromHandle; + uio_Handle *toHandle; +#define BUFSIZE 0x10000 + struct stat statBuf; + char *buf, *bufPtr; + ssize_t numInBuf, numWritten; + + fromHandler = fromDir->pRoot->handler; + toHandler = toDir->pRoot->handler; + if (toHandler->write == NULL || fromHandler->fstat == NULL || + toHandler->unlink == NULL) { + errno = ENOSYS; + return -1; + } + + fromHandle = (fromHandler->open)(fromDir, fromName, O_RDONLY, 0); + if (fromHandle == NULL) { + // errno is set + return -1; + } + + if ((fromHandler->fstat)(fromHandle, &statBuf) == -1) + return copyError(errno, fromHandler, fromHandle, + toHandler, NULL, NULL, NULL, NULL); + + toHandle = (toHandler->open)(toDir, toName, O_WRONLY | O_CREAT | O_EXCL, + statBuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + if (toHandle == NULL) + return copyError(errno, fromHandler, fromHandle, + toHandler, NULL, NULL, NULL, NULL); + + buf = uio_malloc(BUFSIZE); + // not allocated on the stack, as this function may be called + // from a thread with little stack space. + while (1) { + numInBuf = (fromHandler->read)(fromHandle, buf, BUFSIZE); + if (numInBuf == -1) + { + if (errno == EINTR) + continue; + return copyError (errno, fromHandler, fromHandle, + toHandler, toHandle, toDir, toName, buf); + } + if (numInBuf == 0) + break; + + bufPtr = buf; + do { + numWritten = (toHandler->write)(toHandle, bufPtr, numInBuf); + if (numWritten == -1) + { + if (errno == EINTR) + continue; + return copyError (errno, fromHandler, fromHandle, + toHandler, toHandle, toDir, toName, buf); + } + numInBuf -= numWritten; + bufPtr += numWritten; + } while (numInBuf > 0); + } + + uio_free(buf); + (toHandler->close)(toHandle); + (fromHandler->close)(fromHandle); + return 0; +} + +/* + * Closes fromHandle if it's not -1. + * Closes fromHandle if it's not -1. + * Removes 'toName' from 'toDir' if it's not NULL. + * Frees 'buf' if not NULL. + * Always returns -1, setting errno to 'error'. + */ +static int +copyError(int error, + uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle, + uio_FileSystemHandler *toHandler, uio_Handle *toHandle, + uio_PDirHandle *toDir, const char *toName, char *buf) { +#ifdef DEBUG + fprintf(stderr, "Error while copying: %s\n", strerror(error)); +#endif + + if (fromHandle != NULL) + (fromHandler->close)(fromHandle); + + if (toHandle != NULL) + (toHandler->close)(toHandle); + + if (toName != NULL) + (toHandler->unlink)(toDir, toName); + + if (buf != NULL) + uio_free(buf); + + errno = error; + return -1; +} + +/* + * Description: find PDirHandle and MountInfo structures for reading and + * writing for a path from a DirHandle. + * This can be used for opening a file and creating directories. + * Arguments: dirHandle - the directory to which the path is relative. + * path - the path to the component that is to be accessed. + * The last component does not have to exist. + * flags - used to specify what kind of access is requested + * Either O_RDONLY, O_RDWR, O_WRONLY. They may be + * OR'd with other values accepted by open(). These + * are ignored. + * XXX: this is no longer true. + * TODO: update this doc, and check uio_open() and + * perhaps others (uio_mkdir()) for unnecessary + * checks on O_CREAT and O_EXCL. + * extraFlags - either 0 or uio_GPA_NOWRITE + * When 0, the path will be created if it doesn't + * exist in the writing location, but does exist + * in the reading location. With uio_GPA_NOWRITE, it + * won't be created, and -1 will be returned and errno + * will be set to ENOENT. + * mountInfoReadPtr - pointer to location where the pointer + * to the MountInfo structure for the reading location + * should be stored. + * readPDirHandlePtr - pointer to the location where the pointer + * to the PDirHandle used for reading should be stored. + * readPRootPath - pointer to the location where the pointer + * to the physical path to the reading location + * is to be stored. + * The caller is responsible for freeing this. + * Ignored if NULL. + * mountInfoWritePtr - pointer to location where the pointer + * to the MountInfo structure for the writing location + * should be stored. + * writePDirHandlePtr - pointer to the location where the pointer + * to the PDirHandle used for writing should be stored. + * NULL if O_RDONLY was specified. + * If this is the same dir as the one refered + * to by readPDirHandlePtr, the handles will be the + * same too. + * writePRootPath - pointer to the location where the pointer + * to the physical path to the writing location + * is to be stored. + * The caller is responsible for freeing this. + * Ignored if NULL. + * restPtr - pointer to the location where a newly created + * string with as contents the last component of 'path' + * is to be stored. + * The caller is responsible for freeing this. + * Ignored if NULL. + * Returns: 0 - success + * -1 - failure (errno set) + * NB: This is the function that would most benefit from + * the introduction of LDirs. + * This is also the most messy function. It could + * use some more comments, or a cleanup (if possible). + */ +int +uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path, + int flags, int extraFlags, + uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr, + char **readPRootPathPtr, + uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr, + char **writePRootPathPtr, + char **restPtr) { + char *fullPath; // path from dirHandle with 'path' added + const char *pRootPath; // path from the pRoot of a physical tree + const char *readPRootPath, *writePRootPath; + const char *rest, *readRest; + uio_MountTree *tree; + uio_MountTreeItem *item; + uio_MountTreeItem *readItem, *writeItem; + uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle; + int retVal; + uio_bool entryExists; + // Set if the entry pointed to by path exists (including + // the last component of the path) + + // 'path' is relative to dirHandle. + // Fill 'fullPath' with the absolute path. + if (uio_resolvePath(dirHandle, path, strlen(path), &fullPath) == -1) { + // errno is set + return -1; + } + + // Walk the tree of mount points along 'fullPath'. + // 'tree' will be the part of the tree where we end up when we can go + // no further. tree->pLocs are all the mounts relevant there. + // 'rest' will point within 'fullPath' to what is left, after we can + // walk the tree of mountpoints no further. + uio_findMountTree(dirHandle->repository->mountTree, fullPath, + &tree, &rest); + + readItem = NULL; + readPDirHandle = NULL; + readPRootPath = NULL; + writeItem = NULL; + writePDirHandle = NULL; + writePRootPath = NULL; + readRest = NULL; + // Satisfy compiler. + entryExists = false; + // try all the MountInfo structures in effect for this MountTree + for (item = tree->pLocs; item != NULL; item = item->next) { + pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath); + retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath, + strlen(pRootPath), &pDirHandle, &rest); + // rest points inside fullPath + if (retVal == 0) { + // Even the last component appeared to be a dir. + // As the last component did exist, we don't go on. + uio_free(fullPath); + uio_PDirHandle_unref(pDirHandle); + if (readPDirHandle != NULL) + uio_PDirHandle_unref(readPDirHandle); + errno = EISDIR; + return -1; + } + // check if this MountTreeItem is suitable for writing + if (writeItem == NULL && !uio_mountInfoIsReadOnly(item->mountInfo)) + writeItem = item; + if (strchr(rest, '/') == NULL) { + // There's only one dir component that was not matched. + uio_PDirEntryHandle *entry; + + // This MountInfo will do for reading, if the file from the last + // component is present in this dir. + entry = uio_getPDirEntryHandle(pDirHandle, rest); + if (entry != NULL) { + // 'rest' exists, and it is not a dir, as otherwise + // uio_getPDirEntryHandle wouldn't have stopped where it did. + uio_PDirEntryHandle_unref(entry); + readItem = item; + if (readPDirHandle != NULL) + uio_PDirHandle_unref(readPDirHandle); + readPDirHandle = pDirHandle; + readPRootPath = pRootPath; + readRest = rest; + entryExists = true; + break; + } else { + // 'rest' doesn't exist + // We're only interested in this dir if we want to write too. + if (((flags & O_ACCMODE) != O_RDONLY) && readItem == NULL) { + // Keep the first one. + readItem = item; + assert(readPDirHandle == NULL); + readPDirHandle = pDirHandle; + readPRootPath = pRootPath; + readRest = rest; + continue; + } + } + } else { + // There is more than one dir component that was not matched. + // If the first component of the non-matched part is a file, + // stop here and don't check lower dirs. + if (retVal == ENOTDIR) { + uio_free(fullPath); + uio_PDirHandle_unref(pDirHandle); + if (readPDirHandle != NULL) + uio_PDirHandle_unref(readPDirHandle); + errno = ENOTDIR; + return -1; + } + } + uio_PDirHandle_unref(pDirHandle); + } // for + // readItem is set to the first readItem for which the path completely + // exists (including the final component). If there's no such readItem, + // it is set to the first path for which the path exists, but without + // the final component (entryExists is false in this case). If there's + // no such path either, it's set to NULL. + + if (readItem == NULL) { + uio_free(fullPath); + errno = ENOENT; + return -1; + } + if ((flags & O_ACCMODE) == O_RDONLY) { + // write access is not needed + *mountInfoReadPtr = readItem->mountInfo; + *readPDirHandlePtr = readPDirHandle; + if (readPRootPathPtr != NULL) + *readPRootPathPtr = joinPathsAbsolute( + readItem->mountInfo->dirName, readPRootPath); + // Don't touch mountInfoWritePtr and writePDirHandlePtr. + // they'd be NULL. + *restPtr = uio_strdup(readRest); + uio_free(fullPath); + return 0; + } else { + if (entryExists) { + if ((flags & O_CREAT) && (flags & O_EXCL)) { + // An entry should be created, but it already exists and + // it may not be overwritten. + uio_PDirHandle_unref(readPDirHandle); + uio_free(fullPath); + errno = EEXIST; + return -1; + } + } else { + // Though the path to the entry existed (readPDirHandle is + // set to it), the entry itself doesn't, so we can't use it + // unless we intend to create it. + if (flags & O_CREAT) { + // The entry does not exist, but we can create it. + // Handled below. + } else { + // O_CREAT was not specified, so we cannot create + // this entry. + uio_PDirHandle_unref(readPDirHandle); + uio_free(fullPath); + errno = ENOENT; + return -1; + } + + } + } + if (writeItem == readItem) { + // The read directory is usable as write directory too. + *mountInfoReadPtr = readItem->mountInfo; + *readPDirHandlePtr = readPDirHandle; + if (readPRootPathPtr != NULL) + *readPRootPathPtr = joinPathsAbsolute( + readItem->mountInfo->dirName, readPRootPath); + *mountInfoWritePtr = writeItem->mountInfo; + // writeItem == readItem + uio_PDirHandle_ref(readPDirHandle); + *writePDirHandlePtr = readPDirHandle; + // No copy&paste error, the read PDirHandle is the write + // pDirHandle too. + if (writePRootPathPtr != NULL) + *writePRootPathPtr = joinPathsAbsolute( + writeItem->mountInfo->dirName, writePRootPath); + if (restPtr != NULL) + *restPtr = uio_strdup(readRest); + uio_free(fullPath); + return 0; + } + if (writeItem == NULL) { + uio_free(fullPath); + uio_PDirHandle_unref(readPDirHandle); + errno = EPERM; + // readItem is not NULL, so ENOENT would not be correct here. + return -1; + } + + // Left is the case where the write location is different from the + // read location. + + pRootPath = uio_mountTreeItemRestPath(writeItem, tree->lastComp, + fullPath); + + rest = strrchr(pRootPath, '/'); + // rest points inside fullPath + if (rest == NULL) { + rest = pRootPath; + uio_PDirHandle_ref(writeItem->mountInfo->pDirHandle); + writePDirHandle = writeItem->mountInfo->pDirHandle; + } else { + // There exists no path for a write dir, so it will have to be created. + // writeMountInfo indicates the physical tree where it should end up. + + if (extraFlags & uio_GPA_NOWRITE) { + // The caller has specified that the path should not be created. + uio_PDirHandle_unref(readPDirHandle); + uio_free(fullPath); + errno = ENOENT; + return -1; + } + + writePDirHandle = uio_makePath(writeItem->mountInfo->pDirHandle, + pRootPath, rest - pRootPath, 0777); + if (writePDirHandle == NULL) { + int savedErrno; + if (errno == ENOSYS) { + // mkdir not supported. We want to report that we failed + // because of an error in the underlying layer. + // EIO sounds like the best choice. + errno = EIO; + } + savedErrno = errno; + uio_PDirHandle_unref(readPDirHandle); + uio_free(fullPath); + errno = savedErrno; + return -1; + } + rest++; // skip the '/' + } + + if (!entryExists) { + // The path to the read dir exists, but the entry itself doesn't. + // After we created the write dir, the same thing holds for + // the write dir. As it occurs in an earlier MountItem, we'll use + // the writeItem (and writePDirHandle) for reading too. + readItem = writeItem; + uio_PDirHandle_ref(writePDirHandle); + uio_PDirHandle_unref(readPDirHandle); + readPDirHandle = writePDirHandle; + } + + *mountInfoReadPtr = readItem->mountInfo; + *readPDirHandlePtr = readPDirHandle; + if (readPRootPathPtr != NULL) + *readPRootPathPtr = joinPathsAbsolute( + readItem->mountInfo->dirName, readPRootPath); + *mountInfoWritePtr = writeItem->mountInfo; + *writePDirHandlePtr = writePDirHandle; + if (writePRootPathPtr != NULL) + *writePRootPathPtr = joinPathsAbsolute( + writeItem->mountInfo->dirName, writePRootPath); + if (restPtr != NULL) + *restPtr = uio_strdup(rest); + uio_free(fullPath); + return 0; +} + + +/** + * Get handles to the (existing) physical dirs that are effective in a + * path 'path' relative from 'dirHandle' + * + * @param[in] pDirHandle The physical directory to which 'path' is + * relative. + * @param[in] path The path to get the physical dirs for, relative to + * 'pDirHandle' + * @param[in] pathLen The string length of 'path'. + * @param[out] resPDirHandles *resPDirHandles is set to the handles to the + * (existing) physical dirs that are effective in 'path' (relative to + * pDirHandle), or NULL if there are none. + * @param[out] resNumPDirHandles The number of PDirHandles found. + * @param[out] resItems If 'resItems' != NULL, *resItems is set to the + * MountTreeItems belonging to $pDirHandles, or NULL if none were found. + * + * @retval 0 if everything went ok. + * @retval -1 if an error occurred; #errno is set. + */ +int +uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path, + size_t pathLen, uio_PDirHandle ***resPDirHandles, + int *resNumPDirHandles, uio_MountTreeItem ***resItems) { + uio_PDirHandle **pDirHandles; + char *fullPath; // path from dirHandle with 'path' added + uio_MountTree *tree; + const char *rest; + uio_MountTreeItem *item, **items; + const char *pRootPath; // path from the pRoot of a physical tree + int numPDirHandles; + int pDirI; // PDirHandle iterator + + // Determine the absolute path from 'path', which is relative to dirHandle. + if (uio_resolvePath(dirHandle, path, pathLen, &fullPath) == -1) { + // errno is set + return -1; + } + + // get the MountTree effective for the path + uio_findMountTree(dirHandle->repository->mountTree, fullPath, + &tree, &rest); + + // fill pDirHandles with all the PDirHandles for the path + numPDirHandles = uio_mountTreeCountPLocs(tree); + pDirHandles = uio_malloc(numPDirHandles * sizeof (uio_PDirHandle *)); + if (resItems != NULL) { + items = uio_malloc(numPDirHandles * sizeof (uio_MountTreeItem *)); + } else { + items = NULL; // satisfy compiler + } + pDirI = 0; + for (item = tree->pLocs; item != NULL; item = item->next) { + uio_PDirHandle *pDirHandle; + + pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath); + switch (uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath, + strlen(pRootPath), &pDirHandle, &rest)) { + case 0: + // complete path was matched + pDirHandles[pDirI] = pDirHandle; + if (resItems != NULL) + items[pDirI] = item; + pDirI++; + continue; + case ENOENT: + // some component couldn't be matched + uio_PDirHandle_unref(pDirHandle); + continue; + case ENOTDIR: + // next component was not a dir + // Don't look further at other mount Items. + uio_PDirHandle_unref(pDirHandle); + break; + default: + assert(false); + uio_PDirHandle_unref(pDirHandle); + continue; + } + break; + } + numPDirHandles = pDirI; + + uio_free(fullPath); + + *resPDirHandles = uio_realloc(pDirHandles, + numPDirHandles * sizeof (uio_PDirHandle *)); + if (resItems != NULL) + *resItems = uio_realloc(items, + numPDirHandles * sizeof (uio_MountTreeItem *)); + *resNumPDirHandles = numPDirHandles; + + return 0; +} + +// returns 0 if the path is valid and exists +// returns -1 if the path is not valid or does not exist. +// in this case errno will be set to: +// ENOENT if some component didn't exist +// ENOTDIR is some component exists, but is not a dir +// something else (like EPATHTOOLONG) for internal errors +// On success, 'resolvedPath' will be set to the absolute path as returned by +// uio_resolvePath. +int +uio_verifyPath(uio_DirHandle *dirHandle, const char *path, + char **resolvedPath) { + uio_MountTree *tree; + uio_MountTreeItem *item; + const char *rest; + int retVal; + + // TODO: "////", "/somedir//", and "//somedir" are accepted as valid + + // Determine the absolute path from 'path' which is relative to dirHandle. + if (uio_resolvePath(dirHandle, path, strlen(path), resolvedPath) == -1) { + // errno is set + return -1; + } + + // get the MountTree effective for the path + uio_findMountTree(dirHandle->repository->mountTree, *resolvedPath, + &tree, &rest); + + if (rest[0] == '\0') { + // Complete match. Even if there are no pLocs in effect here + // (which can only happen in case the mount Tree is empty and + // we're viewing '/'). + return 0; + } + + // Try all the MountInfo structures in effect for this MountTree. + for (item = tree->pLocs; item != NULL; item = item->next) { + const char *pRootPath; + uio_PDirHandle *pDirHandle; + + pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, + *resolvedPath); + retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath, + strlen(pRootPath), &pDirHandle, &rest); + uio_PDirHandle_unref(pDirHandle); + switch (retVal) { + case 0: + // Complete match. We're done. + return 0; + case ENOTDIR: + // A component is matched, but not as a dir. Failed. + uio_free(*resolvedPath); + errno = ENOTDIR; + return -1; + case ENOENT: + // No match; try the next pLoc. + continue; + default: + // Unknown error. Let's bail out just to be safe. +#ifdef DEBUG + fprintf(stderr, "Warning: Unknown error from " + "uio_walkPhysicalPath: %s\n", strerror(retVal)); +#endif + uio_free(*resolvedPath); + errno = retVal; + return -1; + } + } + + // No match, exit with ENOENT. + uio_free(*resolvedPath); + errno = ENOENT; + return -1; +} + +/** + * Determine the absolute path given a path relative to a given directory. + * + * @param[in] dirHandle The directory to which 'path' is relative. + * @param[in] path The path, relative to 'dirHandle', to make + * absolute. + * @param[in] pathLen The string length of 'path'. + * @param[out] destPath Filled with a newly allocated string containing + * the sought absolute path. It will not contain a '/' as the first + * or last character. It should be freed with uio_free(). + * Unmodified if an error occurs. + * + * @returns the length of '*destPath', or -1 if an error occurs, in which + * case #errno will be set. + */ +ssize_t +uio_resolvePath(uio_DirHandle *dirHandle, const char *path, size_t pathLen, + char **destPath) { + size_t len; + const char *pathEnd, *start, *end; + int numUp; // number of ".." dirs still need to be matched. + char *buffer; + char *endBufPtr; + uio_bool absolute; + + absolute = path[0] == '/'; + pathEnd = path + pathLen; + + // Pass 1: count the amount of space needed + len = 0; + numUp = 0; + for (getLastPathComponent(path, pathEnd, &start, &end); + end > path; + getPreviousPathComponent(path, &start, &end)) { + if (start[0] == '.') { + if (start + 1 == end) { + // "." matched + continue; + } + if (start[1] == '.' && start + 2 == end) { + // ".." matched + numUp++; + continue; + } + } + if (numUp > 0) { + // last 'numUp' components were ".." + numUp--; + continue; + } + len += (end - start) + 1; + // the 1 is for the '/' + } + + // The part from 'dirHandle->path' to 'dirHandle->rootEnd' is + // always copied (for a valid path). The rest we'll have to count. + // (Note the 'rootEnd' in the initialiser of the for loop.) + len += (dirHandle->rootEnd - dirHandle->path); + if (!absolute) { + for (getLastPath0Component(dirHandle->rootEnd, &start, &end); + end > dirHandle->rootEnd; + getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) { + if (numUp > 0) { + numUp--; + continue; + } + len += (end - start) + 1; + // the 1 is for the '/' + } + } + if (numUp > 0) { + // too many ".." + errno = ENOENT; + return -1; + } + + if (len == 0) { + *destPath = uio_malloc(1); + (*destPath)[0] = '\0'; + return 0; + } + + // len--; // we don't want a '/' at the start + // len++; // for the terminating '\0' + buffer = uio_malloc(len); + + // Pass 2: fill the buffer + endBufPtr = buffer + len - 1; + *endBufPtr = '\0'; + numUp = 0; + for (getLastPathComponent(path, pathEnd, &start, &end); + end > path; + getPreviousPathComponent(path, &start, &end)) { + if (start[0] == '.') { + if (start + 1 == end) { + // "." matched + continue; + } + if (start[1] == '.' && start + 2 == end) { + // ".." matched + numUp++; + continue; + } + } + if (numUp > 0) { + // last 'numUp' components were ".." + numUp--; + continue; + } + endBufPtr -= (end - start); + memcpy(endBufPtr, start, end - start); + if (endBufPtr != buffer) { + // We want no '/' at the beginning + endBufPtr--; + *endBufPtr = '/'; + } else { + // We're already done. We might as well take advantage of + // the fact that we know that and exit immediatly: + *destPath = buffer; + return len; + } + } + // copy the part from dirHandle->path to dirHandle->rootEnd + endBufPtr -= (dirHandle->rootEnd - dirHandle->path); + memcpy(endBufPtr, dirHandle->path, dirHandle->rootEnd - dirHandle->path); + if (!absolute) { + // copy (some of) the components from dirHandle->rootEnd on. + for (getLastPath0Component(dirHandle->rootEnd, &start, &end); + end > dirHandle->rootEnd; + getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) { + if (numUp > 0) { + numUp--; + continue; + } + endBufPtr -= (end - start); + memcpy(endBufPtr, start, end - start); + if (endBufPtr != buffer) { + // We want no '/' at the beginning + endBufPtr--; + *endBufPtr = '/'; + } else { + // We're already done. We might as well take advantage of + // the fact that we know that and exit immediatly: + break; + } + } + } + + *destPath = buffer; + return len; +} + + diff --git a/src/libs/uio/ioaux.h b/src/libs/uio/ioaux.h new file mode 100644 index 0000000..41c7e39 --- /dev/null +++ b/src/libs/uio/ioaux.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_IOAUX_H_ +#define LIBS_UIO_IOAUX_H_ + +#include "iointrn.h" +#include "physical.h" +#include "uioport.h" + +int uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path, + size_t pathLen, uio_PDirHandle **endPDirHandle, + const char **pathRest); +uio_PDirHandle *uio_makePath(uio_PDirHandle *pDirHandle, const char *path, + size_t pathLen, mode_t mode); +int uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName, + uio_PDirHandle *toDir, const char *toName); +int uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path, + int flags, int extraFlags, + uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr, + char **readPRootPathPtr, + uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr, + char **writePRootPathPtr, + char **restPtr); +#define uio_GPA_NOWRITE 1 +int uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path, + size_t pathLen, uio_PDirHandle ***resPDirHandles, + int *resNumPDirHandles, uio_MountTreeItem ***resItems); +int uio_verifyPath(uio_DirHandle *dirHandle, const char *path, + char **resolvedPath); +ssize_t uio_resolvePath(uio_DirHandle *dirHandle, const char *path, + size_t pathLen, char **destPath); + + +#endif /* LIBS_UIO_IOAUX_H_ */ + diff --git a/src/libs/uio/iointrn.h b/src/libs/uio/iointrn.h new file mode 100644 index 0000000..522ce18 --- /dev/null +++ b/src/libs/uio/iointrn.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_IOINTRN_H_ +#define LIBS_UIO_IOINTRN_H_ + +#define uio_INTERNAL + +typedef struct uio_PDirEntryHandle uio_PDirEntryHandle; +typedef struct uio_PDirHandle uio_PDirHandle; +typedef struct uio_PFileHandle uio_PFileHandle; +typedef struct uio_EntriesContext uio_EntriesContext; + +#ifndef uio_INTERNAL_PHYSICAL +typedef void *uio_PDirHandleExtra; +typedef void *uio_PFileHandleExtra; +#endif + +#include "io.h" +#include "uioport.h" +#include "physical.h" +#include "mount.h" +#include "mounttree.h" +#include "match.h" +#include "mem.h" + +struct uio_Handle { + int ref; + struct uio_PRoot *root; + uio_NativeHandle native; + int openFlags; + // need to know whether the handle is a RO or RW handle. +}; + +struct uio_DirHandle { + int ref; + struct uio_Repository *repository; + char *path; + // does not contain any '.' or '..'; does not start or end + // with a / + char *rootEnd; + // points to the end of the part of path that is considered + // the root dir. (you can't use '..' to get above the root dir) +}; + +struct uio_MountHandle { + struct uio_Repository *repository; + struct uio_MountInfo *mountInfo; +}; + +struct uio_DirList { + const char **names; + int numNames; + char *buffer; +}; + + +#define uio_PHandle_COMMON \ + int flags; \ + int ref; \ + uio_PRoot *pRoot; + +#define uio_PDirEntryHandle_TYPE_REG 0x0000 +#define uio_PDirEntryHandle_TYPE_DIR 0x0001 +#define uio_PDirEntryHandle_TYPEMASK 0x0001 + +struct uio_PDirEntryHandle { + uio_PHandle_COMMON +}; + +struct uio_PDirHandle { + uio_PHandle_COMMON + uio_PDirHandleExtra extra; +}; + +struct uio_PFileHandle { + uio_PHandle_COMMON + uio_PFileHandleExtra extra; +}; + +struct uio_EntriesContext { + uio_PRoot *pRoot; + uio_NativeEntriesContext native; +}; + + +uio_Handle *uio_Handle_new(uio_PRoot *root, uio_NativeHandle native, + int openFlags); +void uio_Handle_delete(uio_Handle *handle); +void uio_DirHandle_delete(uio_DirHandle *dirHandle); + +uio_PDirEntryHandle *uio_getPDirEntryHandle( + const uio_PDirHandle *dirHandle, const char *name); +void uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle); +void uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle); + +uio_PDirHandle *uio_PDirHandle_new(uio_PRoot *pRoot, + uio_PDirHandleExtra extra); +uio_PFileHandle *uio_PFileHandle_new(uio_PRoot *pRoot, + uio_PFileHandleExtra extra); +void uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle); +void uio_PDirHandle_delete(uio_PDirHandle *pDirHandle); +void uio_PFileHandle_delete(uio_PFileHandle *pFileHandle); + + +static inline void +uio_Handle_ref(uio_Handle *handle) { + handle->ref++; +} + +static inline void +uio_Handle_unref(uio_Handle *handle) { + assert(handle->ref > 0); + handle->ref--; + if (handle->ref == 0) + uio_Handle_delete(handle); +} + +static inline void +uio_DirHandle_ref(uio_DirHandle *dirHandle) { + dirHandle->ref++; +} + +static inline void +uio_DirHandle_unref(uio_DirHandle *dirHandle) { + assert(dirHandle->ref > 0); + dirHandle->ref--; + if (dirHandle->ref == 0) + uio_DirHandle_delete(dirHandle); +} + +static inline uio_bool +uio_PDirEntryHandle_isDir(const uio_PDirEntryHandle *handle) { + return (handle->flags & uio_PDirEntryHandle_TYPEMASK) == + uio_PDirEntryHandle_TYPE_DIR; +} + +static inline void +uio_PDirEntryHandle_ref(uio_PDirEntryHandle *handle) { + handle->ref++; +} + +static inline void +uio_PDirEntryHandle_unref(uio_PDirEntryHandle *handle) { + assert(handle->ref > 0); + handle->ref--; + if (handle->ref == 0) + uio_PDirEntryHandle_delete(handle); +} + +static inline void +uio_PDirHandle_ref(uio_PDirHandle *handle) { + handle->ref++; +} + +static inline void +uio_PDirHandle_unref(uio_PDirHandle *handle) { + assert(handle->ref > 0); + handle->ref--; + if (handle->ref == 0) + uio_PDirHandle_delete(handle); +} + +static inline void +uio_PFileHandle_ref(uio_PFileHandle *handle) { + handle->ref++; +} + +static inline void +uio_PFileHandle_unref(uio_PFileHandle *handle) { + assert(handle->ref > 0); + handle->ref--; + if (handle->ref == 0) + uio_PFileHandle_delete(handle); +} + + +#endif /* LIBS_UIO_IOINTRN_H_ */ + + diff --git a/src/libs/uio/match.c b/src/libs/uio/match.c new file mode 100644 index 0000000..68e6897 --- /dev/null +++ b/src/libs/uio/match.c @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <string.h> +#ifdef DEBUG +# include <stdio.h> +#endif + +#define match_INTERNAL + +#include "match.h" +#include "mem.h" +#include "uioport.h" + +#ifdef HAVE_GLOB +# include <fnmatch.h> +#endif + + +static inline match_MatchContext *match_allocMatchContext(void); +static inline void match_freeMatchContext(match_MatchContext *context); + +static inline match_LiteralContext *match_newLiteralContext(char *pattern); +static inline match_LiteralContext *match_allocLiteralContext(void); +static inline void match_freeLiteralContext(match_LiteralContext *context); +static inline match_PrefixContext *match_newPrefixContext(char *pattern); +static inline match_PrefixContext *match_allocPrefixContext(void); +static inline void match_freePrefixContext(match_PrefixContext *context); +static inline match_SuffixContext *match_newSuffixContext( + char *pattern, size_t len); +static inline match_SuffixContext *match_allocSuffixContext(void); +static inline void match_freeSuffixContext(match_SuffixContext *context); +static inline match_SubStringContext *match_newSubStringContext( + char *pattern); +static inline match_SubStringContext *match_allocSubStringContext(void); +static inline void match_freeSubStringContext(match_SubStringContext *context); +#ifdef HAVE_GLOB +static inline match_GlobContext *match_newGlobContext(char *pattern); +static inline match_GlobContext *match_allocGlobContext(void); +static inline void match_freeGlobContext(match_GlobContext *context); +#endif /* HAVE_GLOB */ +#ifdef HAVE_REGEX +static inline match_RegexContext *match_newRegexContext(void); +static inline match_RegexContext *match_allocRegexContext(void); +static inline void match_freeRegexContext(match_RegexContext *context); +#endif /* HAVE_REGEX */ + + +// *** General part *** + +static inline match_MatchContext * +match_allocMatchContext(void) { + return uio_malloc(sizeof (match_MatchContext)); +} + +static inline void +match_freeMatchContext(match_MatchContext *context) { + uio_free(context); +} + +// NB: Even if this function fails, *contextPtr contains a context +// which needs to be freed. +match_Result +match_prepareContext(const char *pattern, match_MatchContext **contextPtr, + match_MatchType type) { + match_Result result; + + *contextPtr = match_allocMatchContext(); + (*contextPtr)->type = type; + switch (type) { + case match_MATCH_LITERAL: + result = match_prepareLiteral(pattern, + &(*contextPtr)->u.literal); + break; + case match_MATCH_PREFIX: + result = match_preparePrefix(pattern, &(*contextPtr)->u.prefix); + break; + case match_MATCH_SUFFIX: + result = match_prepareSuffix(pattern, &(*contextPtr)->u.suffix); + break; + case match_MATCH_SUBSTRING: + result = match_prepareSubString(pattern, + &(*contextPtr)->u.subString); + break; +#ifdef HAVE_GLOB + case match_MATCH_GLOB: + result = match_prepareGlob(pattern, &(*contextPtr)->u.glob); + break; +#endif +#ifdef HAVE_REGEX + case match_MATCH_REGEX: + result = match_prepareRegex(pattern, &(*contextPtr)->u.regex); + break; +#endif + default: +#ifdef DEBUG + fprintf(stderr, "match_prepareContext called with unsupported " + "type %d matching.\n", type); +#endif + return match_ENOSYS; + } + return result; +} + +match_Result +match_matchPattern(match_MatchContext *context, const char *string) { + switch (context->type) { + case match_MATCH_LITERAL: + return match_matchLiteral(context->u.literal, string); + case match_MATCH_PREFIX: + return match_matchPrefix(context->u.prefix, string); + case match_MATCH_SUFFIX: + return match_matchSuffix(context->u.suffix, string); + case match_MATCH_SUBSTRING: + return match_matchSubString(context->u.subString, string); +#ifdef HAVE_GLOB + case match_MATCH_GLOB: + return match_matchGlob(context->u.glob, string); +#endif +#ifdef HAVE_REGEX + case match_MATCH_REGEX: + return match_matchRegex(context->u.regex, string); +#endif + default: + abort(); + } +} + +// context may be NULL +const char * +match_errorString(match_MatchContext *context, match_Result result) { + switch (result) { + case match_OK: + case match_MATCH: +// case match_NOMATCH: // same value as match_OK + return "Success"; + case match_ENOSYS: + return "Not implemented"; + case match_ENOTINIT: + return "Uninitialised use"; + case match_ECUSTOM: + // Depends on match type. Printed below. + break; + default: + return "Unknown error"; + } + + if (context == NULL) + return "Unknown match-type specific error."; + // We can't be any more specific if no 'context' is supplied. + + switch (context->type) { +#if 0 + case match_MATCH_LITERAL: + return match_errorStringLiteral(context->u.literal, result); + case match_MATCH_PREFIX: + return match_errorStringPrefix(context->u.prefix, result); + case match_MATCH_SUFFIX: + return match_errorStringSuffix(context->u.suffix, result); + case match_MATCH_SUBSTRING: + return match_errorStringSubString(context->u.subString, result); +#ifdef HAVE_GLOB + case match_MATCH_GLOB: + return match_errorStringGlob(context->u.glob, result); +#endif +#endif +#ifdef HAVE_REGEX + case match_MATCH_REGEX: + return match_errorStringRegex(context->u.regex, result); +#endif + default: + abort(); + } +} + +void +match_freeContext(match_MatchContext *context) { + switch (context->type) { + case match_MATCH_LITERAL: + match_freeLiteral(context->u.literal); + break; + case match_MATCH_PREFIX: + match_freePrefix(context->u.prefix); + break; + case match_MATCH_SUFFIX: + match_freeSuffix(context->u.suffix); + break; + case match_MATCH_SUBSTRING: + match_freeSubString(context->u.subString); + break; +#ifdef HAVE_GLOB + case match_MATCH_GLOB: + match_freeGlob(context->u.glob); + break; +#endif +#ifdef HAVE_REGEX + case match_MATCH_REGEX: + match_freeRegex(context->u.regex); + break; +#endif + default: + abort(); + } + match_freeMatchContext(context); +} + +match_Result +match_matchPatternOnce(const char *pattern, match_MatchType type, + const char *string) { + match_MatchContext *context; + match_Result result; + + result = match_prepareContext(pattern, &context, type); + if (result != match_OK) + goto out; + + result = match_matchPattern(context, string); + +out: + match_freeContext(context); + return result; +} + + +// *** Literal part *** + +match_Result +match_prepareLiteral(const char *pattern, + match_LiteralContext **contextPtr) { + *contextPtr = match_newLiteralContext(uio_strdup(pattern)); + return match_OK; +} + +match_Result +match_matchLiteral(match_LiteralContext *context, const char *string) { + return (strcmp(context->pattern, string) == 0) ? + match_MATCH : match_NOMATCH; +} + +void +match_freeLiteral(match_LiteralContext *context) { + uio_free(context->pattern); + match_freeLiteralContext(context); +} + +static inline match_LiteralContext * +match_newLiteralContext(char *pattern) { + match_LiteralContext *result; + + result = match_allocLiteralContext(); + result->pattern = pattern; + return result; +} + +static inline match_LiteralContext * +match_allocLiteralContext(void) { + return uio_malloc(sizeof (match_LiteralContext)); +} + +static inline void +match_freeLiteralContext(match_LiteralContext *context) { + uio_free(context); +} + + +// *** Prefix part *** + +match_Result +match_preparePrefix(const char *pattern, + match_PrefixContext **contextPtr) { + *contextPtr = match_newPrefixContext(uio_strdup(pattern)); + return match_OK; +} + +match_Result +match_matchPrefix(match_PrefixContext *context, const char *string) { + char *patPtr; + + patPtr = context->pattern; + while (1) { + if (*patPtr == '\0') { + // prefix has completely matched + return match_MATCH; + } + if (*string == '\0') { + // no more string left, and still prefix to match + return match_NOMATCH; + } + if (*patPtr != *string) + return match_NOMATCH; + patPtr++; + string++; + } +} + +void +match_freePrefix(match_PrefixContext *context) { + uio_free(context->pattern); + match_freePrefixContext(context); +} + +static inline match_PrefixContext * +match_newPrefixContext(char *pattern) { + match_PrefixContext *result; + + result = match_allocPrefixContext(); + result->pattern = pattern; + return result; +} + +static inline match_PrefixContext * +match_allocPrefixContext(void) { + return uio_malloc(sizeof (match_PrefixContext)); +} + +static inline void +match_freePrefixContext(match_PrefixContext *context) { + uio_free(context); +} + + +// *** Suffix part *** + +match_Result +match_prepareSuffix(const char *pattern, + match_SuffixContext **contextPtr) { + *contextPtr = match_newSuffixContext( + uio_strdup(pattern), strlen(pattern)); + return match_OK; +} + +match_Result +match_matchSuffix(match_SuffixContext *context, const char *string) { + size_t stringLen; + + stringLen = strlen(string); + if (stringLen < context->len) { + // Supplied suffix is larger than string + return match_NOMATCH; + } + + return memcmp(string + stringLen - context->len, context->pattern, + context->len) == 0 ? match_MATCH : match_NOMATCH; +} + +void +match_freeSuffix(match_SuffixContext *context) { + uio_free(context->pattern); + match_freeSuffixContext(context); +} + +static inline match_SuffixContext * +match_newSuffixContext(char *pattern, size_t len) { + match_SuffixContext *result; + + result = match_allocSuffixContext(); + result->pattern = pattern; + result->len = len; + return result; +} + +static inline match_SuffixContext * +match_allocSuffixContext(void) { + return uio_malloc(sizeof (match_SuffixContext)); +} + +static inline void +match_freeSuffixContext(match_SuffixContext *context) { + uio_free(context); +} + + +// *** SubString part *** + +match_Result +match_prepareSubString(const char *pattern, + match_SubStringContext **contextPtr) { + *contextPtr = match_newSubStringContext(uio_strdup(pattern)); + return match_OK; +} + +match_Result +match_matchSubString(match_SubStringContext *context, const char *string) { + return strstr(string, context->pattern) != NULL; +} + +void +match_freeSubString(match_SubStringContext *context) { + uio_free(context->pattern); + match_freeSubStringContext(context); +} + +static inline match_SubStringContext * +match_newSubStringContext(char *pattern) { + match_SubStringContext *result; + + result = match_allocSubStringContext(); + result->pattern = pattern; + return result; +} + +static inline match_SubStringContext * +match_allocSubStringContext(void) { + return uio_malloc(sizeof (match_SubStringContext)); +} + +static inline void +match_freeSubStringContext(match_SubStringContext *context) { + uio_free(context); +} + + +// *** Glob part *** + +#ifdef HAVE_GLOB +match_Result +match_prepareGlob(const char *pattern, match_GlobContext **contextPtr) { + *contextPtr = match_newGlobContext(uio_strdup(pattern)); + return match_OK; +} + +match_Result +match_matchGlob(match_GlobContext *context, const char *string) { + int retval; + + retval = fnmatch(context->pattern, string, 0); + switch (retval) { + case 0: + return match_MATCH; + case FNM_NOMATCH: + return match_NOMATCH; +#if 0 + case FNM_NOSYS: + return match_ENOSYS; +#endif + default: + return match_EUNKNOWN; + } +} + +void +match_freeGlob(match_GlobContext *context) { + uio_free(context->pattern); + match_freeGlobContext(context); +} + +static inline match_GlobContext * +match_newGlobContext(char *pattern) { + match_GlobContext *result; + + result = match_allocGlobContext(); + result->pattern = pattern; + return result; +} + +static inline match_GlobContext * +match_allocGlobContext(void) { + return uio_malloc(sizeof (match_GlobContext)); +} + +static inline void +match_freeGlobContext(match_GlobContext *context) { + uio_free(context); +} +#endif /* HAVE_GLOB */ + + +// *** Regex part *** + +#ifdef HAVE_REGEX +match_Result +match_prepareRegex(const char *pattern, match_RegexContext **contextPtr) { + *contextPtr = match_newRegexContext(); + (*contextPtr)->errorCode = regcomp(&(*contextPtr)->native, pattern, + REG_EXTENDED | REG_NOSUB); + if ((*contextPtr)->errorCode == 0) { + (*contextPtr)->flags = match_REGEX_INITIALISED; + return match_OK; + } + return match_ECUSTOM; +} + +match_Result +match_matchRegex(match_RegexContext *context, const char *string) { + int retval; + + if ((context->flags & match_REGEX_INITIALISED) != + match_REGEX_INITIALISED) { + return match_ENOTINIT; + } + if (context->errorString) { + uio_free(context->errorString); + context->errorString = NULL; + } + retval = regexec(&context->native, string, 0, NULL, 0); + switch (retval) { + case 0: + return match_MATCH; + case REG_NOMATCH: + return match_NOMATCH; + default: + context->errorCode = retval; + return match_ECUSTOM; + } +} + +const char * +match_errorStringRegex(match_RegexContext *context, int errorCode) { + size_t errorStringLength; + + if (context->errorString != NULL) + uio_free(context->errorString); + + errorStringLength = regerror(context->errorCode, &context->native, + NULL, 0); + context->errorString = uio_malloc(errorStringLength); + regerror(context->errorCode, &context->native, + context->errorString, errorStringLength); + + (void) errorCode; + return context->errorString; +} + +void +match_freeRegex(match_RegexContext *context) { + regfree(&context->native); + if (context->errorString) + uio_free(context->errorString); + match_freeRegexContext(context); +} + +static inline match_RegexContext * +match_newRegexContext(void) { + match_RegexContext *result; + result = match_allocRegexContext(); + result->errorString = NULL; + result->errorCode = 0; + result->flags = 0; + return result; +} + +static inline match_RegexContext * +match_allocRegexContext(void) { + return uio_malloc(sizeof (match_RegexContext)); +} + +static inline void +match_freeRegexContext(match_RegexContext *context) { + uio_free(context); +} +#endif /* HAVE_REGEX */ + diff --git a/src/libs/uio/match.h b/src/libs/uio/match.h new file mode 100644 index 0000000..0557e7d --- /dev/null +++ b/src/libs/uio/match.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_MATCH_H_ +#define LIBS_UIO_MATCH_H_ + +typedef struct match_MatchContext match_MatchContext; + +#include <sys/types.h> + +// TODO: make this into a configurable option +//#define HAVE_GLOB +#define HAVE_REGEX + + +typedef enum { + match_MATCH_LITERAL = 0, + match_MATCH_PREFIX, + match_MATCH_SUFFIX, + match_MATCH_SUBSTRING, +#ifdef HAVE_GLOB + match_MATCH_GLOB, +#endif +#ifdef HAVE_REGEX + match_MATCH_REGEX, +#endif +} match_MatchType; + +typedef int match_Result; +#define match_NOMATCH 0 +#define match_MATCH 1 +#define match_OK 0 +#define match_EUNKNOWN -1 +#define match_ENOSYS -2 +#define match_ECUSTOM -3 +#define match_ENOTINIT -4 + +typedef struct match_LiteralContext match_LiteralContext; +typedef struct match_PrefixContext match_PrefixContext; +typedef struct match_SuffixContext match_SuffixContext; +typedef struct match_SubStringContext match_SubStringContext; +#ifdef HAVE_GLOB +typedef struct match_GlobContext match_GlobContext; +#endif +#ifdef HAVE_REGEX +typedef struct match_RegexContext match_RegexContext; +#endif + +match_Result match_prepareContext(const char *pattern, + match_MatchContext **contextPtr, match_MatchType type); +match_Result match_matchPattern(match_MatchContext *context, + const char *string); +const char *match_errorString(match_MatchContext *context, + match_Result result); +void match_freeContext(match_MatchContext *context); +match_Result match_matchPatternOnce(const char *pattern, match_MatchType type, + const char *string); + + +/* *** Internal definitions follow *** */ +#ifdef match_INTERNAL + +#include <sys/types.h> +#ifdef HAVE_REGEX +# include <regex.h> +#endif + +#include "uioport.h" + +struct match_MatchContext { + match_MatchType type; + union { + match_LiteralContext *literal; + match_PrefixContext *prefix; + match_SuffixContext *suffix; + match_SubStringContext *subString; +#ifdef HAVE_GLOB + match_GlobContext *glob; +#endif +#ifdef HAVE_REGEX + match_RegexContext *regex; +#endif + } u; +}; + +struct match_LiteralContext { + char *pattern; +}; + +struct match_PrefixContext { + char *pattern; +}; + +struct match_SuffixContext { + char *pattern; + size_t len; + // for speed +}; + +struct match_SubStringContext { + char *pattern; +}; + +#ifdef HAVE_GLOB +struct match_GlobContext { + char *pattern; +}; +#endif + +#ifdef HAVE_REGEX +struct match_RegexContext { + regex_t native; + char *errorString; + int errorCode; + int flags; +#define match_REGEX_INITIALISED 1 +}; +#endif + +match_Result match_prepareLiteral(const char *pattern, + match_LiteralContext **contextPtr); +match_Result match_matchLiteral(match_LiteralContext *context, + const char *string); +void match_freeLiteral(match_LiteralContext *context); + +match_Result match_preparePrefix(const char *pattern, + match_PrefixContext **contextPtr); +match_Result match_matchPrefix(match_PrefixContext *context, + const char *string); +void match_freePrefix(match_PrefixContext *context); + +match_Result match_prepareSuffix(const char *pattern, + match_SuffixContext **contextPtr); +match_Result match_matchSuffix(match_SuffixContext *context, + const char *string); +void match_freeSuffix(match_SuffixContext *context); + +match_Result match_prepareSubString(const char *pattern, + match_SubStringContext **contextPtr); +match_Result match_matchSubString(match_SubStringContext *context, + const char *string); +void match_freeSubString(match_SubStringContext *context); + +#ifdef HAVE_GLOB +match_Result match_prepareGlob(const char *pattern, + match_GlobContext **contextPtr); +match_Result match_matchGlob(match_GlobContext *context, + const char *string); +void match_freeGlob(match_GlobContext *context); +#endif /* HAVE_GLOB */ + +#ifdef HAVE_REGEX +match_Result match_prepareRegex(const char *pattern, + match_RegexContext **contextPtr); +match_Result match_matchRegex(match_RegexContext *context, + const char *string); +const char *match_errorStringRegex(match_RegexContext *context, + int errorCode); +void match_freeRegex(match_RegexContext *context); +#endif + +#endif /* match_INTERNAL */ + +#endif /* LIBS_UIO_MATCH_H_ */ + diff --git a/src/libs/uio/mem.h b/src/libs/uio/mem.h new file mode 100644 index 0000000..915837e --- /dev/null +++ b/src/libs/uio/mem.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_MEM_H_ +#define LIBS_UIO_MEM_H_ + +#include <stdlib.h> +#include <string.h> +#include "uioport.h" + +#define uio_malloc malloc +#define uio_realloc realloc +#define uio_free free +#define uio_calloc calloc + +#ifdef uio_MEM_DEBUG +// When uio_strdup is defined to the libc strdup, there's no opportunity +// to intercept the alloc. Hence this function here. +static inline char * +uio_strdup(const char *s) { + char *result; + size_t size; + + size = strlen(s) + 1; + result = uio_malloc(size); + memcpy(result, s, size); + return result; +} +#else +# define uio_strdup strdup +#endif + +// Allocates new memory, copies 'len' characters from 'src', and adds a '\0'. +static inline char * +uio_memdup0(const char *src, size_t len) { + char *dst = uio_malloc(len + 1); + memcpy(dst, src, len); + dst[len] = '\0'; + return dst; +} + +#endif + + diff --git a/src/libs/uio/memdebug.c b/src/libs/uio/memdebug.c new file mode 100644 index 0000000..b6bd1b6 --- /dev/null +++ b/src/libs/uio/memdebug.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <assert.h> +#include <stdio.h> + +#include "memdebug.h" +#include "hashtable.h" +#include "mem.h" +#include "uioutils.h" +#include "types.h" +#include "uioport.h" + +// I'm intentionally not including the .h files. It would only make things +// messy, because the second arguments would be other pointers, which +// would require typecasts of the functions in uio_MemDebug_logTypeInfo. +extern void uio_DirHandle_print(FILE *out, const void *); +extern void uio_printMountInfo(FILE *out, const void *); +extern void uio_printMountTreeItem(FILE *out, const void *); +extern void uio_printPathComp(FILE *out, const void *); + +// Must be in the same order as in uio_MemDebug_LogTypes +// Change the third field to have debug info printed for specific actions. +// See memdebug.h file the possible values. +const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[] = { + { "uio_DirHandle", uio_DirHandle_print, 0 }, + { "uio_FileSystemInfo", NULL, 0 }, + { "uio_GPDir", NULL, 0 }, + { "uio_GPFile", NULL, 0 }, + { "uio_GPRoot", NULL, 0 }, + { "uio_Handle", NULL, 0 }, + { "uio_MountHandle", NULL, 0 }, + { "uio_MountInfo", uio_printMountInfo, 0 }, + { "uio_MountTree", NULL, 0 }, + { "uio_MountTreeItem", uio_printMountTreeItem, 0 }, + { "uio_PathComp", uio_printPathComp, 0 }, + { "uio_PFileHandle", NULL, 0 }, + { "uio_PDirHandle", NULL, 0 }, + { "uio_PRoot", NULL, 0 }, + { "uio_Repository", NULL, 0 }, + { "uio_Stream", NULL, 0 }, + { "stdio_GPDirData", NULL, 0 }, +#ifdef HAVE_ZIP + { "zip_GPFileData", NULL, 0 }, + { "zip_GPDirData", NULL, 0 }, +#endif +}; + +HashTable_HashTable **uio_MemDebug_logs; + + +static uio_uint32 uio_MemDebug_pointerHash(const void *ptr); +static uio_bool uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2); +static void *uio_MemDebug_pointerCopy(const void *ptr); +static void uio_MemDebug_pointerFree(void *ptr); + +static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_new(int ref); +static inline void uio_MemDebug_PointerInfo_delete( + uio_MemDebug_PointerInfo *pointerInfo); +static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_alloc(void); +static inline void uio_MemDebug_PointerInfo_free( + uio_MemDebug_PointerInfo *pointerInfo); + +void +uio_MemDebug_init(void) { + int i; + + assert((int) uio_MemDebug_numLogTypes == + (sizeof uio_MemDebug_logTypeInfo / + sizeof uio_MemDebug_logTypeInfo[0])); + uio_MemDebug_logs = uio_malloc(uio_MemDebug_numLogTypes * + sizeof (HashTable_HashTable *)); + + for (i = 0; i < uio_MemDebug_numLogTypes; i++) { + uio_MemDebug_logs[i] = HashTable_newHashTable( + uio_MemDebug_pointerHash, + uio_MemDebug_pointerCompare, + uio_MemDebug_pointerCopy, + uio_MemDebug_pointerFree, + 4, 0.85, 0.90); + } +} + +void +uio_MemDebug_unInit(void) { + int i; + + for (i = 0; i < uio_MemDebug_numLogTypes; i++) + HashTable_deleteHashTable(uio_MemDebug_logs[i]); + + uio_free(uio_MemDebug_logs); +} + +static uio_uint32 +uio_MemDebug_pointerHash(const void *ptr) { + uio_uintptr ptrInt; + + ptrInt = (uio_uintptr) ptr; + return (uio_uint32) (ptrInt ^ (ptrInt >> 10) ^ (ptrInt >> 20)); +} + +static uio_bool +uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2) { + return ptr1 == ptr2; +} + +static void * +uio_MemDebug_pointerCopy(const void *ptr) { + return unconst(ptr); +} + +static void +uio_MemDebug_pointerFree(void *ptr) { + (void) ptr; +} + +void +uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr) { + uio_MemDebug_PointerInfo *pointerInfo; + + if (ptr == NULL) { + fprintf(stderr, "Fatal: Allocated pointer is (%s *) NULL.\n", + uio_MemDebug_logTypeInfo[(int) type].name); + abort(); + } + if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_ALLOC) { + fprintf(stderr, "Alloc "); + uio_MemDebug_printPointer(stderr, type, ptr); + fprintf(stderr, "\n"); + } + pointerInfo = uio_MemDebug_PointerInfo_new(1); + HashTable_add(uio_MemDebug_logs[type], ptr, (void *) pointerInfo); +} + +void +uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr) { + uio_MemDebug_PointerInfo *pointerInfo; + + if (ptr == NULL) { + fprintf(stderr, "Fatal: Attempt to free (%s *) NULL pointer.\n", + uio_MemDebug_logTypeInfo[(int) type].name); + abort(); + } + if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_FREE) { + fprintf(stderr, "Free "); + uio_MemDebug_printPointer(stderr, type, ptr); + fprintf(stderr, "\n"); + } + pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr); + if (pointerInfo == NULL) { + fprintf(stderr, "Fatal: Attempt to free unallocated pointer " + "(%s *) %p.\n", + uio_MemDebug_logTypeInfo[(int) type].name, ptr); + abort(); + } +#if 0 + if (pointerInfo->ref != 0) { + fprintf(stderr, "Fatal: Attempt to free pointer with references " + "left (%s *) %p.\n", + uio_MemDebug_logTypeInfo[(int) type].name, ptr); + abort(); + } +#endif + uio_MemDebug_PointerInfo_free(pointerInfo); + HashTable_remove(uio_MemDebug_logs[type], ptr); +} + +void +uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr) { + uio_MemDebug_PointerInfo *pointerInfo; + + if (ptr == NULL) { + fprintf(stderr, "Fatal: Attempt to increment reference to a " + "(%s *) NULL pointer.\n", + uio_MemDebug_logTypeInfo[(int) type].name); + abort(); + } + pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr); + if (pointerInfo == NULL) { + fprintf(stderr, "Fatal: Attempt to increment reference to " + "unallocated pointer (%s *) %p.\n", + uio_MemDebug_logTypeInfo[(int) type].name, ptr); + abort(); + } + pointerInfo->pointerRef++; + if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_REF) { + fprintf(stderr, "Ref++ to %d, ", pointerInfo->pointerRef); + uio_MemDebug_printPointer(stderr, type, ptr); + fprintf(stderr, "\n"); + } +} + +void +uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr) { + uio_MemDebug_PointerInfo *pointerInfo; + + if (ptr == NULL) { + fprintf(stderr, "Fatal: Attempt to decrement reference to a " + "(%s *) NULL pointer.\n", + uio_MemDebug_logTypeInfo[(int) type].name); + abort(); + } + pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr); + if (pointerInfo == NULL) { + fprintf(stderr, "Fatal: Attempt to decrement reference to " + "unallocated pointer (%s *) %p.\n", + uio_MemDebug_logTypeInfo[(int) type].name, ptr); + abort(); + } + if (pointerInfo->pointerRef == 0) { + fprintf(stderr, "Fatal: Attempt to decrement reference below 0 for " + "pointer (%s *) %p.\n", + uio_MemDebug_logTypeInfo[(int) type].name, ptr); + abort(); + } + pointerInfo->pointerRef--; + if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_UNREF) { + fprintf(stderr, "Ref-- to %d, ", pointerInfo->pointerRef); + uio_MemDebug_printPointer(stderr, type, ptr); + fprintf(stderr, "\n"); + } +} + +void +uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type, void *ptr) { + fprintf(out, "(%s *) %p", uio_MemDebug_logTypeInfo[(int) type].name, ptr); + if (uio_MemDebug_logTypeInfo[(int) type].printFunction != NULL) { + fprintf(out, ": "); + uio_MemDebug_logTypeInfo[(int) type].printFunction(out, ptr); + } +} + +void +uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type) { + HashTable_Iterator *iterator; + + for (iterator = HashTable_getIterator(uio_MemDebug_logs[type]); + !HashTable_iteratorDone(iterator); + iterator = HashTable_iteratorNext(iterator)) { + uio_MemDebug_printPointer(out, type, HashTable_iteratorKey(iterator)); + fprintf(out, "\n"); + } + HashTable_freeIterator(iterator); +} + +void +uio_MemDebug_printPointers(FILE *out) { + int i; + + for (i = 0; i < uio_MemDebug_numLogTypes; i++) + uio_MemDebug_printPointersType(out, i); +} + +static inline uio_MemDebug_PointerInfo * +uio_MemDebug_PointerInfo_new(int ref) { + uio_MemDebug_PointerInfo *result; + result = uio_MemDebug_PointerInfo_alloc(); + result->pointerRef = ref; + return result; +} + +static inline void +uio_MemDebug_PointerInfo_delete(uio_MemDebug_PointerInfo *pointerInfo) { + uio_MemDebug_PointerInfo_free(pointerInfo); +} + +static inline uio_MemDebug_PointerInfo * +uio_MemDebug_PointerInfo_alloc(void) { + return uio_malloc(sizeof (uio_MemDebug_PointerInfo)); +} + +static inline void +uio_MemDebug_PointerInfo_free(uio_MemDebug_PointerInfo *pointerInfo) { + uio_free(pointerInfo); +} + diff --git a/src/libs/uio/memdebug.h b/src/libs/uio/memdebug.h new file mode 100644 index 0000000..e04a8d2 --- /dev/null +++ b/src/libs/uio/memdebug.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_MEMDEBUG_H_ +#define LIBS_UIO_MEMDEBUG_H_ + +#include <stdio.h> +#include "uioport.h" + +// Important: if you add an item here, add it to uio_MemDebug_LogTypeInfo +// too. The order should be the same. +typedef enum { + uio_MemDebug_LogType_uio_DirHandle, + uio_MemDebug_LogType_uio_FileSystemInfo, + uio_MemDebug_LogType_uio_GPDir, + uio_MemDebug_LogType_uio_GPFile, + uio_MemDebug_LogType_uio_GPRoot, + uio_MemDebug_LogType_uio_Handle, + uio_MemDebug_LogType_uio_MountHandle, + uio_MemDebug_LogType_uio_MountInfo, + uio_MemDebug_LogType_uio_MountTree, + uio_MemDebug_LogType_uio_MountTreeItem, + uio_MemDebug_LogType_uio_PathComp, + uio_MemDebug_LogType_uio_PFileHandle, + uio_MemDebug_LogType_uio_PDirHandle, + uio_MemDebug_LogType_uio_PRoot, + uio_MemDebug_LogType_uio_Repository, + uio_MemDebug_LogType_uio_Stream, + uio_MemDebug_LogType_stdio_GPDirData, + uio_MemDebug_LogType_zip_GPFileData, + uio_MemDebug_LogType_zip_GPDirData, + + uio_MemDebug_numLogTypes, /* This needs to be the last entry */ +} uio_MemDebug_LogType; + +typedef void (uio_MemDebug_PrintFunction)(FILE *out, const void *arg); + +typedef struct uio_MemDebug_LogTypeInfo { + const char *name; + uio_MemDebug_PrintFunction *printFunction; + int flags; +#define uio_MemDebug_PRINT_ALLOC 0x1 +#define uio_MemDebug_PRINT_FREE 0x2 +#define uio_MemDebug_PRINT_REF 0x4 +#define uio_MemDebug_PRINT_UNREF 0x8 +#define uio_MemDebug_PRINT_ALL \ + (uio_MemDebug_PRINT_ALLOC | uio_MemDebug_PRINT_FREE | \ + uio_MemDebug_PRINT_REF | uio_MemDebug_PRINT_UNREF) +} uio_MemDebug_LogTypeInfo; + +extern const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[]; + +typedef struct uio_MemDebug_PointerInfo { + int pointerRef; + // Ref counter for the associated pointer, not the pointerInfo + // itself. +} uio_MemDebug_PointerInfo; + +void uio_MemDebug_init(void); +void uio_MemDebug_unInit(void); +void uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr); +void uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr); +void uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr); +void uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr); +void uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type, + void *ptr); +void uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type); +void uio_MemDebug_printPointers(FILE *out); + +#define uio_MemDebug_debugAlloc(type, pointer) \ + uio_MemDebug_logAllocation(uio_MemDebug_LogType_ ## type, pointer) +#define uio_MemDebug_debugFree(type, pointer) \ + uio_MemDebug_logDeallocation(uio_MemDebug_LogType_ ## type, pointer) +#define uio_MemDebug_debugRef(type, pointer) \ + uio_MemDebug_logRef(uio_MemDebug_LogType_ ## type, pointer) +#define uio_MemDebug_debugUnref(type, pointer) \ + uio_MemDebug_logUnref(uio_MemDebug_LogType_ ## type, pointer) + +#endif /* LIBS_UIO_MEMDEBUG_H_ */ + diff --git a/src/libs/uio/mount.c b/src/libs/uio/mount.c new file mode 100644 index 0000000..f095d3f --- /dev/null +++ b/src/libs/uio/mount.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <assert.h> +#include <string.h> + +#include "iointrn.h" +#include "uioport.h" +#include "mount.h" +#include "mounttree.h" +#include "mem.h" +#include "uioutils.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +static void uio_Repository_delete(uio_Repository *repository); +static uio_Repository *uio_Repository_alloc(void); +static void uio_Repository_free(uio_Repository *repository); + + +void +uio_repositoryAddMount(uio_Repository *repository, + uio_MountInfo *mountInfo, uio_MountLocation location, + uio_MountInfo *relative) { + lockRepository(repository, uio_LOCK_WRITE); + switch (location) { + case uio_MOUNT_TOP: { + uio_MountInfo **newMounts; + + newMounts = uio_malloc( + (repository->numMounts + 2) * sizeof (uio_MountInfo *)); + newMounts[0] = mountInfo; + memcpy(&newMounts[1], repository->mounts, + (repository->numMounts + 1) * sizeof (uio_MountInfo *)); + uio_free(repository->mounts); + repository->mounts = newMounts; + repository->numMounts++; + break; + } + case uio_MOUNT_BOTTOM: + repository->mounts = uio_realloc(repository->mounts, + (repository->numMounts + 2) * sizeof (uio_MountInfo *)); + repository->mounts[repository->numMounts] = mountInfo; + repository->numMounts++; + break; + case uio_MOUNT_ABOVE: { + int i; + uio_MountInfo **newMounts; + + i = 0; + while (repository->mounts[i] != relative) + i++; + newMounts = (uio_MountInfo **) insertArrayPointer( + (const void **) repository->mounts, + repository->numMounts + 1, i, (void *) mountInfo); + uio_free(repository->mounts); + repository->mounts = newMounts; + repository->numMounts++; + break; + } + case uio_MOUNT_BELOW: { + int i; + uio_MountInfo **newMounts; + + i = 0; + while (repository->mounts[i] != relative) + i++; + i++; + newMounts = (uio_MountInfo **) insertArrayPointer( + (const void **) repository->mounts, + repository->numMounts + 1, i, (void *) mountInfo); + uio_free(repository->mounts); + repository->mounts = newMounts; + repository->numMounts++; + break; + } + default: + assert(false); + } + unlockRepository(repository); +} + +void +uio_repositoryRemoveMount(uio_Repository *repository, uio_MountInfo *mountInfo) { + int i; + uio_MountInfo **newMounts; + + lockRepository(repository, uio_LOCK_WRITE); + + i = 0; + while (repository->mounts[i] != mountInfo) + i++; + newMounts = (uio_MountInfo **) excludeArrayPointer( + (const void **) repository->mounts, repository->numMounts + 1, + i, 1); + uio_free(repository->mounts); + repository->mounts = newMounts; + repository->numMounts--; + unlockRepository(repository); +} + +// sets ref to 1 +uio_Repository * +uio_Repository_new(int flags) { + uio_Repository *result; + + result = uio_Repository_alloc(); + result->ref = 1; + result->flags = flags; + result->numMounts = 0; + result->mounts = uio_malloc(1 * sizeof (uio_MountInfo *)); + result->mounts[0] = NULL; + result->mountTree = uio_makeRootMountTree(); + return result; +} + +void +uio_Repository_unref(uio_Repository *repository) { + assert(repository->ref > 0); + repository->ref--; + if (repository->ref == 0) + uio_Repository_delete(repository); +} + +static uio_Repository * +uio_Repository_alloc(void) { + uio_Repository *result = uio_malloc(sizeof (uio_Repository)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_Repository, (void *) result); +#endif + return result; +} + +static void +uio_Repository_delete(uio_Repository *repository) { + assert(repository->numMounts == 0); + uio_free(repository->mounts); + uio_MountTree_delete(repository->mountTree); + uio_Repository_free(repository); +} + +static void +uio_Repository_free(uio_Repository *repository) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_Repository, (void *) repository); +#endif + uio_free(repository); +} + + diff --git a/src/libs/uio/mount.h b/src/libs/uio/mount.h new file mode 100644 index 0000000..237f54b --- /dev/null +++ b/src/libs/uio/mount.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_MOUNT_H_ +#define LIBS_UIO_MOUNT_H_ + + +typedef struct uio_Repository uio_Repository; +typedef struct uio_AutoMount uio_AutoMount; +#define uio_MOUNT_RDONLY (1 << 1) + +/* *** Internal definitions follow */ +#ifdef uio_INTERNAL + +#define uio_MOUNT_LOCATION_MASK (3 << 2) + +#include "uioport.h" +#include "fstypes.h" +#include "mounttree.h" +#include "match.h" + +struct uio_Repository { + int ref; /* reference counter */ + int flags; + int numMounts; + struct uio_MountInfo **mounts; + // first in the list is considered the top + // last entry is NULL + struct uio_MountTree *mountTree; +}; + +#define lockRepository(repository, prot) +#define unlockRepository(repository) + +uio_Repository *uio_Repository_new(int flags); +void uio_Repository_unref(uio_Repository *repository); +void uio_repositoryAddMount(uio_Repository *repository, + uio_MountInfo *mountInfo, uio_MountLocation location, + uio_MountInfo *relative); +void uio_repositoryRemoveMount(uio_Repository *repository, + uio_MountInfo *mountInfo); + + +#endif /* uio_INTERNAL */ + +#endif /* LIBS_UIO_MOUNT_H_ */ + diff --git a/src/libs/uio/mounttree.c b/src/libs/uio/mounttree.c new file mode 100644 index 0000000..eb5bdec --- /dev/null +++ b/src/libs/uio/mounttree.c @@ -0,0 +1,814 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <stdlib.h> +#include <string.h> +#ifdef DEBUG +# include <stdio.h> +#endif +#include <assert.h> + +#include "iointrn.h" +#include "uioport.h" +#include "mounttree.h" +#include "paths.h" +#include "types.h" +#include "mem.h" +#include "uioutils.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +static uio_MountTree *uio_mountTreeAddMountInfoRecTree( + uio_Repository *repository, uio_MountTree *tree, + uio_MountInfo *mountInfo, const char *start, const char *end, + uio_PathComp *upComp, uio_MountLocation location, + const uio_MountInfo *relative); +static inline uio_MountTree *uio_mountTreeAddMountInfoRecTreeSub( + uio_Repository *repository, uio_MountTree **tree, + uio_MountInfo *mountInfo, const char *start, + const char *end, uio_MountLocation location, + const uio_MountInfo *relative); +static void uio_mountTreeAddMountInfoLocAll(uio_Repository *repository, + uio_MountTree *tree, uio_MountInfo *mountInfo, int depth, + uio_MountLocation location, const uio_MountInfo *relative); +static uio_MountTreeItem *uio_copyMountTreeItems(uio_MountTreeItem *item, + int extraDepth); +static void uio_addMountTreeItem(uio_Repository *repository, + uio_MountTreeItem **pLocs, uio_MountTreeItem *item, + uio_MountLocation location, const uio_MountInfo *relative); +static uio_MountTree *uio_mountTreeAddNewSubTree(uio_Repository *repository, + uio_MountTree *tree, const char *path, uio_MountInfo *mountInfo, + uio_PathComp *upComp, uio_MountLocation location, + const uio_MountInfo *relative); +static void uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub); +static uio_MountTree * uio_splitMountTree(uio_MountTree **tree, uio_PathComp + *lastComp, int depth); +static void uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree, + uio_MountInfo *mountInfo); +static void uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo); + +static inline uio_MountTree * uio_MountTree_new(uio_MountTree *subTrees, + uio_MountTreeItem *pLocs, uio_MountTree *upTree, uio_PathComp + *comps, uio_PathComp *lastComp, uio_MountTree *next); +static inline uio_MountTreeItem *uio_MountTree_newItem( + uio_MountInfo *mountInfo, int depth, uio_MountTreeItem *next); + +static inline void uio_MountTreeItem_delete(uio_MountTreeItem *item); + +static inline uio_MountTree *uio_MountTree_alloc(void); +static inline uio_MountTreeItem *uio_MountTreeItem_alloc(void); +static inline uio_MountInfo *uio_MountInfo_alloc(void); + +static inline void uio_MountTree_free(uio_MountTree *mountTree); +static inline void uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem); +static inline void uio_MountInfo_free(uio_MountInfo *mountInfo); + + +// make the root mount Tree +uio_MountTree * +uio_makeRootMountTree(void) { + return uio_MountTree_new(NULL, NULL, NULL, NULL, NULL, NULL); +} + +// Add a MountInfo structure to a MountTree in the place pointed +// to by 'path'. +// returns the MountTree for the location at the end of the path +uio_MountTree * +uio_mountTreeAddMountInfo(uio_Repository *repository, uio_MountTree *mountTree, + uio_MountInfo *mountInfo, const char *path, uio_MountLocation location, + const uio_MountInfo *relative) { + const char *start, *end; + + getFirstPath0Component(path, &start, &end); + return uio_mountTreeAddMountInfoRecTree(repository, mountTree, mountInfo, + start, end, NULL, location, relative); +} + +// recursive helper for uio_mountTreeAddMountInfo +// returns the MountTree for the location at the end of the path +static uio_MountTree * +uio_mountTreeAddMountInfoRecTree(uio_Repository *repository, uio_MountTree *tree, + uio_MountInfo *mountInfo, const char *start, const char *end, + uio_PathComp *upComp, + uio_MountLocation location, const uio_MountInfo *relative) { + uio_MountTree **sub; + + if (*start == '\0') { + // End of the path. Put the MountInfo here and on all subtrees below + // this level. + uio_mountTreeAddMountInfoLocAll(repository, tree, mountInfo, 0, + location, relative); + return tree; + } + + // Check if sub trees match the path. + for (sub = &tree->subTrees; *sub != NULL; sub = &(*sub)->next) { + uio_MountTree *resTree; + + resTree = uio_mountTreeAddMountInfoRecTreeSub(repository, sub, + mountInfo, start, end, location, relative); + if (resTree != NULL) { + // handled + return resTree; + } + } + // No subtree found matching (part of) 'path'. + + // Need to add a new tree sub + return uio_mountTreeAddNewSubTree(repository, tree, start, mountInfo, + upComp, location, relative); +} + +// recursive helper for uio_mountTreeAddMountInfo +// Pre: *start != '\0' +// returns the MountTree for the location at the end of the path, if +// that falls within this tree. If not, returns NULL. +static inline uio_MountTree * +uio_mountTreeAddMountInfoRecTreeSub(uio_Repository *repository, + uio_MountTree **tree, uio_MountInfo *mountInfo, + const char *start, const char *end, uio_MountLocation location, + const uio_MountInfo *relative) { + uio_PathComp *comp, *lastComp; + int depth; + + comp = (*tree)->comps; + if (strncmp(comp->name, start, end - start) != 0 || + comp->name[end - start] != '\0') { + // first component does not match; this is not the correct subTree + return NULL; + } + + depth = 1; + // try to match all components of the directory path to this subTree. + while (1) { + getNextPath0Component(&start, &end); + lastComp = comp; + comp = comp->next; + + if (comp == NULL) + break; + + if (*start == '\0') { + // end of the path reached + // We need to split up the components and insert a new + // MountTree here. + uio_MountTree *newTree; + newTree = uio_splitMountTree(tree, lastComp, depth); + + // Add mountInfo to each of the MountTrees below newTree. + uio_mountTreeAddMountInfoLocAll(repository, newTree, mountInfo, 0, + location, relative); + + return newTree; + } + if (strncmp(comp->name, start, end - start) != 0 || + comp->name[end - start] != '\0') { + // Some, but not all components matched; we need to split + // up the components and add a new subTree here for the + // (non-matching) rest of the path. + uio_MountTree *newTree; + + newTree = uio_splitMountTree(tree, lastComp, depth); + + // A new Tree is added at the split-point. + return uio_mountTreeAddNewSubTree(repository, newTree, start, + mountInfo, lastComp, location, relative); + } + getNextPath0Component(&start, &end); + depth++; + } + + // All components matched. We can recurse to the next subdir. + return uio_mountTreeAddMountInfoRecTree(repository, *tree, mountInfo, + start, end, lastComp, location, relative); +} + +// Add a MountInfo struct 'mountInfo' to the pLocs fields of all subTrees +// starting with 'tree'. +// 'depth' is the distance to the MountTree where the MountInfo is located. +static void +uio_mountTreeAddMountInfoLocAll(uio_Repository *repository, uio_MountTree *tree, + uio_MountInfo *mountInfo, int depth, uio_MountLocation location, + const uio_MountInfo *relative) { + uio_MountTreeItem *newPLoc; + uio_MountTree *subTree; + int compCount; + + // Add a new PLoc to this mountTree + newPLoc = uio_MountTree_newItem(mountInfo, depth, NULL); + uio_addMountTreeItem(repository, &tree->pLocs, newPLoc, location, relative); + + // Recurse for subtrees + for (subTree = tree->subTrees; subTree != NULL; + subTree = subTree->next) { + compCount = uio_countPathComps(subTree->comps); + uio_mountTreeAddMountInfoLocAll( + repository, subTree, mountInfo, depth + compCount, + location, relative); + } +} + +// pre: repository->mounts is already updated +// pre: if location is uio_MOUNT_BELOW or uio_MOUNT_ABOVE, 'relative' +// exists in repository->mounts +static void +uio_addMountTreeItem(uio_Repository *repository, uio_MountTreeItem **pLocs, + uio_MountTreeItem *item, + uio_MountLocation location, const uio_MountInfo *relative) { + switch (location) { + case uio_MOUNT_TOP: + item->next = *pLocs; + *pLocs = item; + break; + case uio_MOUNT_BOTTOM: + while (*pLocs != NULL) + pLocs = &(*pLocs)->next; + item->next = NULL; + *pLocs = item; + break; + case uio_MOUNT_ABOVE: { + uio_MountInfo **mountInfo; + mountInfo = repository->mounts; + while (*mountInfo != relative) { + assert(*mountInfo != NULL); + if ((*pLocs)->mountInfo == *mountInfo) + pLocs = &(*pLocs)->next; + mountInfo++; + } + item->next = *pLocs; + *pLocs = item; + break; + } + case uio_MOUNT_BELOW: { + uio_MountInfo **mountInfo; + mountInfo = repository->mounts; + while (*mountInfo != relative) { + assert(*mountInfo != NULL); + if ((*pLocs)->mountInfo == *mountInfo) + pLocs = &(*pLocs)->next; + mountInfo++; + } + item->next = (*pLocs)->next; + (*pLocs)->next = item; + break; + } + default: + assert(false); + } +} + +// Copy a chain of MountTreeItems, but increase the depth by 'extraDepth'. +static uio_MountTreeItem * +uio_copyMountTreeItems(uio_MountTreeItem *item, int extraDepth) { + uio_MountTreeItem *result, **resPtr; + uio_MountTreeItem *newItem; + + resPtr = &result; + while (item != NULL) { + newItem = uio_MountTree_newItem( + item->mountInfo, item->depth + extraDepth, NULL); + *resPtr = newItem; + resPtr = &newItem->next; + item = item->next; + } + *resPtr = NULL; + return result; +} + +// add a new sub tree under a tree 'tree'. +// 'path' is the part leading up to the new tree and +// 'mountInfo' is the MountInfo structure to at there. +// 'upComp' points to the last path component that lead to 'tree'. +static uio_MountTree * +uio_mountTreeAddNewSubTree(uio_Repository *repository, uio_MountTree *tree, + const char *path, uio_MountInfo *mountInfo, uio_PathComp *upComp, + uio_MountLocation location, const uio_MountInfo *relative) { + uio_MountTreeItem *item, *items; + uio_MountTree *newTree; + uio_PathComp *compList, *lastComp; + int compCount; + + compList = uio_makePathComps(path, upComp); + compCount = uio_countPathComps(compList); + lastComp = uio_lastPathComp(compList); + item = uio_MountTree_newItem(mountInfo, 0, NULL); + item->next = NULL; + items = uio_copyMountTreeItems(tree->pLocs, compCount); + uio_addMountTreeItem(repository, &items, item, location, + relative); + newTree = uio_MountTree_new( + NULL /* subTrees */, + items /* pLocs */, + tree /* upTree */, + compList /* comps */, + lastComp /* lastComp */, + NULL /* next */); + uio_mountTreeAddSub(tree, newTree); + return newTree; +} + +// add a sub structure to the end of the 'subTrees' list of a tree. +static void +uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub) { + uio_MountTree **subPtr; + + for (subPtr = &tree->subTrees; *subPtr != NULL; + subPtr = &(*subPtr)->next) { + // Nothing to do here. + } + *subPtr = sub; +} + +// Add a new MountTree structure in between two MountTrees. +// Tree points to the pointer for the tree in front of which the new +// tree needs to be placed (at depth 'depth'). +// 'lastComp' is the last pathComp of the part before the splitting point +// It returns the new MountTree. +static uio_MountTree * +uio_splitMountTree(uio_MountTree **tree, uio_PathComp *lastComp, int depth) { + uio_MountTree *newTree; + uio_MountTreeItem *items; + + items = uio_copyMountTreeItems((*tree)->upTree->pLocs, depth); + newTree = uio_MountTree_new( + *tree /* subTrees */, + items /* pLocs */, + (*tree)->upTree /* upTree */, + (*tree)->comps /* comps */, + lastComp /* lastComp */, + NULL /* next */); + (*tree)->upTree = newTree; + (*tree)->comps = lastComp->next; + lastComp->next = NULL; + *tree = newTree; + return newTree; +} + +void +uio_mountTreeRemoveMountInfo(uio_Repository *repository, + uio_MountTree *mountTree, uio_MountInfo *mountInfo) { + uio_MountTree **subTreePtr; + uio_MountTree *upTree; + + // If the tree has no sub-trees and it has the same items as the + // upTree, with 'mountInfo' added, then the tree is a dead end + // and can be removed entirely. + // First we handle the other case. + // Note that if the upTree has exactly one item less than the tree + // itself, these items must be the same, plus mountInfo for the + // tree itself, as each tree has at least the items of its upTree. + if (mountTree->upTree == NULL || mountTree->subTrees != NULL || + uio_mountTreeCountPLocs(mountTree) != + uio_mountTreeCountPLocs(mountTree->upTree) + 1) { + // We can't remove the tree itself. + // We need to remove the mountInfo from the tree, and all subTrees. + // Then we're done. + uio_mountTreeRemoveMountInfoRec(mountTree, mountInfo); + return; + } + + // mountTree itself can be removed. + // First remove the tree from the list of subtrees of the upTree. + subTreePtr = &mountTree->upTree->subTrees; + while (1) { + assert(*subTreePtr != NULL); + if (*subTreePtr == mountTree) + break; + subTreePtr = &(*subTreePtr)->next; + } + *subTreePtr = mountTree->next; + + // Save the upTree for later. + upTree = mountTree->upTree; + + // Remove the tree itself. + uio_MountTree_delete(mountTree); + + // The upTree itself could have become unnecessary now. + // This is the case when upTree now only has one subTree, and upTree + // and the subTree have the same items. + // Again, same item count implies same items. + if (upTree->subTrees == NULL || upTree->subTrees->next != NULL || + uio_mountTreeCountPLocs(upTree) != + uio_mountTreeCountPLocs(upTree->subTrees)) { + // upTree is still necessary. We're done. + return; + } + + // Merge upTree and upTree->subTrees. + // It would be easiest to keep upTree, and throw upTree->subTrees away, + // but that's not possible as external links point to upTree->subTrees. + // First merge the path components: + assert(upTree->subTrees->lastComp != NULL); + upTree->subTrees->lastComp->next = upTree->subTrees->comps; + upTree->subTrees->lastComp = upTree->lastComp; + upTree->subTrees->comps = upTree->comps; + // Now let the pointer that pointed to upTree, point to upTree->subTrees. + // Change upTree->next accordingly. + if (upTree->upTree == NULL) { + assert(repository->mountTree == upTree); + repository->mountTree = upTree->subTrees; + // upTree->subTrees->next is already NULL + } else { + uio_MountTree *next; + subTreePtr = &upTree->upTree->subTrees; + while (1) { + assert(*subTreePtr != NULL); + if (*subTreePtr == upTree) + break; + subTreePtr = &(*subTreePtr)->next; + } + next = (*subTreePtr)->next; + *subTreePtr = upTree->subTrees; + upTree->subTrees->next = next; + } + + // Now delete the tree itself + upTree->subTrees = NULL; + upTree->comps = NULL; + uio_MountTree_delete(upTree); +} + +// pre: mountInfo exists in mountTree->pLocs (and hence in pLocs for +// every sub-tree) +static void +uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree, + uio_MountInfo *mountInfo) { + uio_MountTree *subTree; + uio_MountTreeItem **itemPtr, *item; + + // recurse for all subTrees + for (subTree = mountTree->subTrees; subTree != NULL; + subTree = subTree->next) + uio_mountTreeRemoveMountInfoRec(subTree, mountInfo); + + // Find the mount info in this tree. + itemPtr = &mountTree->pLocs; + while (1) { + assert(*itemPtr != NULL); + // We know an item with the specified mountInfo + // must be here somewhere. + if ((*itemPtr)->mountInfo == mountInfo) { + // Found it. + break; + } + itemPtr = &(*itemPtr)->next; + } + + item = *itemPtr; + *itemPtr = item->next; + uio_MountTreeItem_delete(item); +} + +// Count the number of pLocs in a tree that leads to. +int +uio_mountTreeCountPLocs(const uio_MountTree *tree) { + int count; + uio_MountTreeItem *item; + + count = 0; + for (item = tree->pLocs; item != NULL; item = item->next) + count++; + return count; +} + +// resTree may point to top +// pPath may point to path +void +uio_findMountTree(uio_MountTree *top, const char *path, + uio_MountTree **resTree, const char **pPath) { + const char *start, *end, *pathFromTree; + uio_MountTree *tree, *sub; + uio_PathComp *comp; + + getFirstPath0Component(path, &start, &end); + tree = top; + while(1) { + if (*start == '\0') { + *resTree = tree; + *pPath = start; + return; + } + + pathFromTree = start; + sub = tree->subTrees; + while(1) { + if (sub == NULL) { + // No matching sub Dirs found. So we report back the current + // dir. + *resTree = tree; + *pPath = pathFromTree; + return; + } + comp = sub->comps; + if (strncmp(comp->name, start, end - start) == 0 && + comp->name[end - start] == '\0') + break; + sub = sub->next; + } + // Found a Sub dir which matches at least partially. + + while (1) { + getNextPath0Component(&start, &end); + comp = comp->next; + if (comp == NULL) + break; + if (*start == '\0' || + strncmp(comp->name, start, end - start) != 0 || + comp->name[end - start] != '\0') { + // either the path ends here, or the path in the tree does. + // either way, the last Tree is the one we want. + *resTree = tree; + *pPath = pathFromTree; + return; + } + } + // all components matched until the next MountTree + tree = sub; + } +} + +// finds the path to the MountInfo associated with a mountTreeItem +// given a path to the 'item' itself. +// 'item' is the mountTreeItem +// 'endComp' is the last PathComp leading to 'item' +// 'start' is the start of the path to the item +char * +uio_mountTreeItemRestPath(const uio_MountTreeItem *item, + uio_PathComp *endComp, const char *path) { + int i; + const char *pathPtr; + + i = item->depth; + while (i--) + endComp = endComp->up; + + pathPtr = path; + if (endComp != NULL) { + while (1) { + pathPtr += endComp->nameLen; + endComp = endComp->up; + if (endComp == NULL) + break; + pathPtr++; + // for a '/' + } + } + if (*path == '/') { + // / at the beginning of the path + pathPtr++; + } + if (*pathPtr == '/') { + // / at the end of the path + pathPtr++; + } +// return (char *) pathPtr; + // gives warning +// return *((char **)((void *) &pathPtr)); + // not portable + return (char *) unconst((const void *) pathPtr); +} + +void +uio_printMountTree(FILE *outStream, const uio_MountTree *tree, int indent) { + uio_MountTree *sub; + uio_PathComp *comp; + + fprintf(outStream, "("); + uio_printMountTreeItems(outStream, tree->pLocs); + fprintf(outStream, ")\n"); + for (sub = tree->subTrees; sub != NULL; sub = sub->next) { + int newIndent; + + newIndent = indent; + fprintf(outStream, "%*s", indent, ""); + for (comp = sub->comps; comp != NULL; comp = comp->next) { + fprintf(outStream, "/%s", comp->name); + newIndent += 1 + comp->nameLen; + } + fprintf(outStream, " "); + newIndent += 1; + uio_printMountTree(outStream, sub, newIndent); + } +} + +void +uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item) { + uio_printMountInfo(outStream, item->mountInfo); + fprintf(outStream, ":%d", item->depth); +} + +void +uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item) { + if (!item) + return; + while(1) { + uio_printMountTreeItem(outStream, item); + item = item->next; + if (item == NULL) + break; + fprintf(outStream, ", "); + } +} + +void +uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree) { + if (tree->upTree == NULL) { + fprintf(outStream, "/"); + } else + uio_printPathToComp(outStream, tree->lastComp); +} + +void +uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo) { + uio_FileSystemInfo *fsInfo; + + fsInfo = uio_getFileSystemInfo(mountInfo->fsID); + fprintf(outStream, "%s:/%s", fsInfo->name, mountInfo->dirName); +} + +static void +uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo) { + uio_FileSystemInfo *fsInfo; + + fsInfo = uio_getFileSystemInfo(mountInfo->fsID); + fprintf(outStream, "???:%s on ", mountInfo->dirName); + uio_printPathToMountTree(outStream, mountInfo->mountTree); + fprintf(outStream, " type %s (", fsInfo->name); + if (mountInfo->flags & uio_MOUNT_RDONLY) { + fprintf(outStream, "ro"); + } else + fprintf(outStream, "rw"); + fprintf(outStream, ")\n"); +} + +void +uio_printMounts(FILE *outStream, const uio_Repository *repository) { + int i; + + for (i = 0; i < repository->numMounts; i++) { + uio_printMount(outStream, repository->mounts[i]); + } +} + + +// *** uio_MountTree*** // + +static inline uio_MountTree * +uio_MountTree_new(uio_MountTree *subTrees, uio_MountTreeItem *pLocs, + uio_MountTree *upTree, uio_PathComp *comps, uio_PathComp *lastComp, + uio_MountTree *next) { + uio_MountTree *result; + + result = uio_MountTree_alloc(); + result->subTrees = subTrees; + result->pLocs = pLocs; + result->upTree = upTree; + result->comps = comps; + result->lastComp = lastComp; + result->next = next; + return result; +} + +void +uio_MountTree_delete(uio_MountTree *tree) { + uio_MountTree *subTree, *nextTree; + uio_MountTreeItem *item, *nextItem; + + subTree = tree->subTrees; + while (subTree != NULL) { + nextTree = subTree->next; + uio_MountTree_delete(subTree); + subTree = nextTree; + } + + item = tree->pLocs; + while (item != NULL) { + nextItem = item->next; + uio_MountTreeItem_delete(item); + item = nextItem; + } + + if (tree->comps != NULL) + uio_PathComp_delete(tree->comps); + + uio_MountTree_free(tree); +} + +static inline uio_MountTree * +uio_MountTree_alloc(void) { + uio_MountTree *result = uio_malloc(sizeof (uio_MountTree)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_MountTree, (void *) result); +#endif + return result; +} + +static inline void +uio_MountTree_free(uio_MountTree *mountTree) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_MountTree, (void *) mountTree); +#endif + uio_free(mountTree); +} + + +// *** uio_MountTreeItem *** // + +static inline uio_MountTreeItem * +uio_MountTree_newItem(uio_MountInfo *mountInfo, int depth, + uio_MountTreeItem *next) { + uio_MountTreeItem *result; + + result = uio_MountTreeItem_alloc(); + result->mountInfo = mountInfo; + result->depth = depth; + result->next = next; + return result; +} + +static inline void +uio_MountTreeItem_delete(uio_MountTreeItem *item) { + uio_MountTreeItem_free(item); +} + +static inline uio_MountTreeItem * +uio_MountTreeItem_alloc(void) { + uio_MountTreeItem *result = uio_malloc(sizeof (uio_MountTreeItem)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_MountTreeItem, (void *) result); +#endif + return result; +} + +static inline void +uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_MountTreeItem, (void *) mountTreeItem); +#endif + uio_free(mountTreeItem); +} + + +// *** uio_MountInfo *** // + +uio_MountInfo * +uio_MountInfo_new(uio_FileSystemID fsID, uio_MountTree *mountTree, + uio_PDirHandle *pDirHandle, char *dirName, uio_AutoMount **autoMount, + uio_MountHandle *mountHandle, int flags) { + uio_MountInfo *result; + + result = uio_MountInfo_alloc(); + result->fsID = fsID; + result->mountTree = mountTree; + result->pDirHandle = pDirHandle; + result->dirName = dirName; + result->autoMount = autoMount; + result->mountHandle = mountHandle; + result->flags = flags; + return result; +} + +void +uio_MountInfo_delete(uio_MountInfo *mountInfo) { + uio_free(mountInfo->dirName); + uio_PDirHandle_unref(mountInfo->pDirHandle); + uio_MountInfo_free(mountInfo); +} + +static inline uio_MountInfo * +uio_MountInfo_alloc(void) { + uio_MountInfo *result = uio_malloc(sizeof (uio_MountInfo)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_MountInfo, (void *) result); +#endif + return result; +} + +static inline void +uio_MountInfo_free(uio_MountInfo *mountInfo) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_MountInfo, (void *) mountInfo); +#endif + uio_free(mountInfo); +} + + diff --git a/src/libs/uio/mounttree.h b/src/libs/uio/mounttree.h new file mode 100644 index 0000000..b0041bc --- /dev/null +++ b/src/libs/uio/mounttree.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_MOUNTTREE_H_ +#define LIBS_UIO_MOUNTTREE_H_ + +#include <stdio.h> +#include "mount.h" + +void uio_printMounts(FILE *outStream, const uio_Repository *repository); + +/* *** Internal definitions follow *** */ +#ifdef uio_INTERNAL + +#include <sys/types.h> + +typedef struct uio_MountTreeItem uio_MountTreeItem; +typedef struct uio_MountTree uio_MountTree; +typedef struct uio_MountInfo uio_MountInfo; + +#include "physical.h" +#include "types.h" +#include "uioport.h" +#include "paths.h" + + +/* + * A MountTree describes the relation between dirs (PRoot structures) + * mounted in a directory structure. + * A MountTree structure represents a node (a dir) in this directory + * structure. + * It describes what MountInfo structures apply in that dir and below (if + * not overrided in subnodes) (the 'pLocs' field). + * These path components are also linked together 'up'-wards (towards the + * root of the tree) by the 'up' field. + */ + +struct uio_MountTreeItem { + struct uio_MountInfo *mountInfo; + int depth; + // 'mountInfo->pDirHandle' and 'depth' together point to a + // location in a physical tree. An uio_pDirHandle alone can't be + // used as the directory might not exist. + // So pDirHandle points to the top dir that was mounted, and + // 'depth' indicates how many directory names of the path to + // this point in the Mount Tree need to be followed. + // Example: + // This MountTreeItem is somewhere in a tree /foo/bar/bla + // and depth = 1. Then this MountTreeItem points to + // /bla in the specified root. + struct uio_MountTreeItem *next; + // The next MountTreeItem in a MountTree +}; + +struct uio_MountTree { + struct uio_MountTree *subTrees; + // Trees for subdirs in this MountTree + struct uio_MountTreeItem *pLocs; + // The physical locations that have effect in this MountTree. + struct uio_MountTree *upTree; + // the MountTree that pointed to this MountTree + struct uio_PathComp *comps; + // the names of the path components that lead to the tree. + // Not necessary every PathComp is connected to a MountTree. + // If you have /foo and /foo/bar/zut mounted, then + // there are MountTrees for /, /foo and /foo/bar/zut, + // but there are PathComps for 'foo', 'bar' and 'zut'. + struct uio_PathComp *lastComp; + // The last PathComp of comps that pointed to this MountTree. + // This can be used to trace the path back to the top. + struct uio_MountTree *next; + // If this tree is a subTree of a tree, 'next' points to the + // next subTree of that tree. +}; + +/* + * A MountInfo structure describes how a physical structure was mounted. + * A physical structure can be used by several MountInfo structures. + */ +struct uio_MountInfo { + int flags; + /* Mount flags */ +# define uio_MOUNTINFO_RDONLY uio_MOUNT_RDONLY + uio_FileSystemID fsID; + char *dirName; + /* The path inside the mounted fs leading to pDirHandle */ + uio_PDirHandle *pDirHandle; + /* The pDirHandle belonging to this mount */ + uio_MountTree *mountTree; + /* The MountTree node for the mountpoint */ + uio_AutoMount **autoMount; + uio_MountHandle *mountHandle; +}; + + +/* + * Say we've got mounted (in order): + * Bla -> / + * Bar -> /foo/bar + * Foo -> /foo + * Zut -> /zut/123 + * Fop -> /zut/123 + * + * This will build a tree that looks like this: + * (the strings between brackets are the mounted filesystems that have effect + * in a dir, in order) + * + * / (Bar) + * foo (Foo, Bla) + * bar (Foo, Bar, Bla) + * zut/123 (Fop, Zut, Bla) + * + * The MountTree will look like: + * / = { + * sub = { + * /foo, + * /zut/123 + * }, + * pLocs = { + * BlaDir:/ (0) + * } + * } + * /foo = { + * sub = { + * /foo/bar + * }, + * pLocs = { + * BlaDir:/foo (1), + * FooDir:/ (0) + * } + * } + * /foo/bar = { + * sub = { }, + * pLocs = { + * BlaDir:/foo/bar (2), + * BarDir:/ (0), + * FooDir:/bar (1) + * } + * } + * /zut/123 = { + * sub = { }, + * pLocs = { + * FooDir:/ (0) + * ZutDir:/ (0) + * BlaDir:/zut/123 (2) + * } + * } + * + * 'BlaDir:/zut/123 (2)' means the pDirHandle is 'Bla', and the dir into + * that directory is '/zut/123', but as this is always a postfix of the + * path where we are, the number of dirs (the '(2)') is enough to store + * (apart from the pDirHandle). + */ + +uio_MountTree *uio_makeRootMountTree(void); +void uio_MountTree_delete(uio_MountTree *tree); +uio_MountTree *uio_mountTreeAddMountInfo(uio_Repository *repository, + uio_MountTree *mountTree, uio_MountInfo *mountInfo, const char *path, + uio_MountLocation location, const uio_MountInfo *relative); +void uio_mountTreeRemoveMountInfo(uio_Repository *repository, + uio_MountTree *mountTree, uio_MountInfo *mountInfo); +void uio_findMountTree(uio_MountTree *top, const char *path, + uio_MountTree **resTree, const char **pPath); +char *uio_mountTreeItemRestPath(const uio_MountTreeItem *item, + uio_PathComp *endComp, const char *path); +int uio_mountTreeCountPLocs(const uio_MountTree *tree); +uio_MountInfo *uio_MountInfo_new(uio_FileSystemID fsID, + uio_MountTree *mountTree, uio_PDirHandle *pDirHandle, + char *dirName, uio_AutoMount **autoMount, + uio_MountHandle *mountHandle, int flags); +void uio_MountInfo_delete(uio_MountInfo *mountInfo); +void uio_printMountTree(FILE *outStream, const uio_MountTree *tree, + int indent); +void uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item); +void uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item); +void uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree); +void uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo); + +static inline uio_bool +uio_mountInfoIsReadOnly(uio_MountInfo *mountInfo) { + return (mountInfo->flags & uio_MOUNTINFO_RDONLY) != 0; +} + +#endif /* uio_INTERNAL */ + +#endif /* LIBS_UIO_MOUNTTREE_H_ */ + diff --git a/src/libs/uio/paths.c b/src/libs/uio/paths.c new file mode 100644 index 0000000..f8411cd --- /dev/null +++ b/src/libs/uio/paths.c @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "paths.h" +#include "uioport.h" +#include "mem.h" + +static inline uio_PathComp *uio_PathComp_alloc(void); +static inline void uio_PathComp_free(uio_PathComp *pathComp); + +// gets the first dir component of a path +// sets '*start' to the start of the first component +// sets '*end' to the end of the first component +// if *start >= dirEnd, then the end has been reached. +void +getFirstPathComponent(const char *dir, const char *dirEnd, + const char **startComp, const char **endComp) { + assert(dir <= dirEnd); + *startComp = dir; + if (*startComp == dirEnd) { + *endComp = *startComp; + return; + } + *endComp = memchr(*startComp, '/', dirEnd - *startComp); + if (*endComp == NULL) + *endComp = dirEnd; +} + +// gets the first dir component of a path +// sets '*start' to the start of the first component +// sets '*end' to the end of the first component +// if **start == '\0', then the end has been reached. +void +getFirstPath0Component(const char *dir, + const char **startComp, const char **endComp) { + *startComp = dir; + if (**startComp == '\0') { + *endComp = *startComp; + return; + } + *endComp = strchr(*startComp, '/'); + if (*endComp == NULL) + *endComp = *startComp + strlen(*startComp); +} + +// gets the next component of a path +// '*start' should be set to the start of the last component +// '*end' should be set to the end of the last component +// '*start' will be set to the start of the next component +// '*end' will be set to the end of the next component +// if *start >= dirEnd, then the end has been reached. +void +getNextPathComponent(const char *dirEnd, + const char **startComp, const char **endComp) { + assert(*endComp <= dirEnd); + if (*endComp == dirEnd) { + *startComp = *endComp; + return; + } + assert(**endComp == '/'); + *startComp = *endComp + 1; + *endComp = memchr(*startComp, '/', dirEnd - *startComp); + if (*endComp == NULL) + *endComp = dirEnd; +} + +// gets the next component of a path +// '*start' should be set to the start of the last component +// '*end' should be set to the end of the last component +// '*start' will be set to the start of the next component +// '*end' will be set to the end of the next component +// if **start == '\0', then the end has been reached. +void +getNextPath0Component(const char **startComp, const char **endComp) { + if (**endComp == '\0') { + *startComp = *endComp; + return; + } + assert(**endComp == '/'); + *startComp = *endComp + 1; + *endComp = strchr(*startComp, '/'); + if (*endComp == NULL) + *endComp = *startComp + strlen(*startComp); +} + +// if *end == dir, the beginning has been reached +void +getLastPathComponent(const char *dir, const char *endDir, + const char **startComp, const char **endComp) { + *endComp = endDir; +// if (*(*endComp - 1) == '/') +// (*endComp)--; + *startComp = *endComp; + // TODO: use memrchr where available + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// if *end == dir, the beginning has been reached +// pre: dir is \0-terminated +void +getLastPath0Component(const char *dir, + const char **startComp, const char **endComp) { + *endComp = dir + strlen(dir); +// if (*(*endComp - 1) == '/') +// (*endComp)--; + *startComp = *endComp; + // TODO: use memrchr where available + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// if *end == dir, the beginning has been reached +void +getPreviousPathComponent(const char *dir, + const char **startComp, const char **endComp) { + if (*startComp == dir) { + *endComp = *startComp; + return; + } + *endComp = *startComp - 1; + *startComp = *endComp; + while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/') + (*startComp)--; +} + +// Combine two parts of a paths into a new path. +// The new path starts with a '/' only when the first part does. +// The first path may (but doesn't have to) end on a '/', or may be empty. +// Pre: the second path doesn't start with a '/' +char * +joinPaths(const char *first, const char *second) { + char *result, *resPtr; + size_t firstLen, secondLen; + + if (first[0] == '\0') + return uio_strdup(second); + + firstLen = strlen(first); + if (first[firstLen - 1] == '/') + firstLen--; + secondLen = strlen(second); + result = uio_malloc(firstLen + secondLen + 2); + resPtr = result; + + memcpy(resPtr, first, firstLen); + resPtr += firstLen; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, second, secondLen); + resPtr += secondLen; + + *resPtr = '\0'; + return result; +} + +// Combine two parts of a paths into a new path. +// The new path will always start with a '/'. +// The first path may (but doesn't have to) end on a '/', or may be empty. +// Pre: the second path doesn't start with a '/' +char * +joinPathsAbsolute(const char *first, const char *second) { + char *result, *resPtr; + size_t firstLen, secondLen; + + if (first[0] == '\0') { + secondLen = strlen(second); + result = uio_malloc(secondLen + 2); + result[0] = '/'; + memcpy(&result[1], second, secondLen); + result[secondLen + 1] = '\0'; + return result; + } + + firstLen = strlen(first); + if (first[firstLen - 1] == '/') + firstLen--; + secondLen = strlen(second); + result = uio_malloc(firstLen + secondLen + 3); + resPtr = result; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, first, firstLen); + resPtr += firstLen; + + *resPtr = '/'; + resPtr++; + + memcpy(resPtr, second, secondLen); + resPtr += secondLen; + + *resPtr = '\0'; + return result; +} + +// Returns 'false' if +// - one of the path components is empty, or +// - one of the path components is ".", or +// - one of the path components is ".." +// and 'true' otherwise. +uio_bool +validPathName(const char *path, size_t len) { + const char *pathEnd; + const char *start, *end; + + pathEnd = path + len; + getFirstPathComponent(path, pathEnd, &start, &end); + while (start < pathEnd) { + if (end == start || (end - start == 1 && start[0] == '.') || + (end - start == 2 && start[0] == '.' && start[1] == '.')) + return false; + getNextPathComponent(pathEnd, &start, &end); + } + return true; +} + +// returns 0 if the path is not a valid UNC path. +// Does not skip trailing slashes. +size_t +uio_skipUNCServerShare(const char *inPath) { + const char *path = inPath; + + // Skip the initial two backslashes. + if (path[0] != '\\' || path[1] != '\\') + return (size_t) 0; + path += 2; + + // Skip the server part. + while (*path != '\\' && *path != '/') { + if (*path == '\0') + return (size_t) 0; + path++; + } + + // Skip the seperator. + path++; + + // Skip the share part. + while (*path != '\0' && *path != '\\' && *path != '/') + path++; + + return (size_t) (path - inPath); +} + +/** + * Find the server and share part of a Windows "Universal Naming Convention" + * path (a path of the form '\\server\share\path\file'). + * The path must contain at least a server and share path to be valid. + * The initial two slashes must be backslashes, the other slashes may each + * be either a forward slash or a backslash. + * + * @param[in] inPath The path to parse. + * @param[out] outPath Will contain a newly allocated string (to be + * freed using uio_free(), containing the server and share part + * of inPath, separated by a backslash, or NULL if 'inPath' was + * not a valid UNC path. + * @param[out] outLen If not NULL on entry, it will contain the string + * length of '*outPath', or 0 if 'inPath' was not a valid UNC path. + * + * @returns The number of characters to add to 'inPath' to get to the first + * path component past the server and share part, or 0 if 'inPath' + * was not a valid UNC path. + */ +size_t +uio_getUNCServerShare(const char *inPath, char **outPath, size_t *outLen) { + const char *ptr; + const char *server; + const char *serverEnd; + const char *share; + const char *shareEnd; + char *name; + char *nameEnd; + size_t nameLen; + + ptr = inPath; + + if (ptr[0] != '\\' || ptr[1] != '\\') + goto noMatch; + + // Parse the server part. + server = ptr + 2; + serverEnd = server; + for (;;) { + if (*serverEnd == '\0') + goto noMatch; + if (isPathDelimiter(*serverEnd)) + break; + serverEnd++; + } + if (serverEnd == server) + goto noMatch; + + // Parse the share part. + share = serverEnd + 1; + shareEnd = share; + while (*shareEnd != '\0') { + if (isPathDelimiter(*shareEnd)) + break; + serverEnd++; + } + + // Skip any trailing path delimiters. + ptr = shareEnd; + while (isPathDelimiter(*ptr)) + ptr++; + + // Allocate a new string and fill it. + nameLen = (serverEnd - server) + (shareEnd - share) + 3; + name = uio_malloc(nameLen + 1); + nameEnd = name; + *(nameEnd++) = '\\'; + *(nameEnd++) = '\\'; + memcpy(nameEnd, server, serverEnd - server); + *(nameEnd++) = '\\'; + memcpy(nameEnd, share, shareEnd - share); + *nameEnd = '\0'; + + *outPath = name; + if (outLen != NULL) + *outLen = nameLen; + return (size_t) (ptr - inPath); + +noMatch: + *outPath = NULL; + if (outLen != NULL) + *outLen = 0; + return (size_t) 0; +} + +// Decomposes a path into its components. +// If isAbsolute is not NULL, *isAbsolute will be set to true +// iff the path is absolute. +// As POSIX considers multiple consecutive slashes to be equivalent to +// a single slash, so will uio (but not in the "\\MACHINE\share" part +// of a Windows UNC path). +int +decomposePath(const char *path, uio_PathComp **pathComp, + uio_bool *isAbsolute) { + uio_PathComp *result; + uio_PathComp *last; + uio_PathComp **endResult = &result; + uio_bool absolute = false; + char *name; +#ifdef HAVE_UNC_PATHS + size_t nameLen; +#endif /* HAVE_UNC_PATHS */ + + if (path[0] == '\0') { + errno = ENOENT; + return -1; + } + + last = NULL; +#ifdef HAVE_UNC_PATHS + path += uio_getUNCServerShare(path, &name, &nameLen); + if (name != NULL) { + // UNC path + *endResult = uio_PathComp_new(name, nameLen, last); + last = *endResult; + endResult = &last->next; + + absolute = true; + } else +#endif /* HAVE_UNC_PATHS */ +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(path[0]) && path[1] == ':') { + // DOS/Windows drive letter. + if (path[2] != '\0' && !isPathDelimiter(path[2])) { + errno = ENOENT; + return -1; + } + name = uio_memdup0(path, 2); + *endResult = uio_PathComp_new(name, 2, last); + last = *endResult; + endResult = &last->next; + absolute = true; + } else +#endif /* HAVE_DRIVE_LETTERS */ + { + if (isPathDelimiter(*path)) { + absolute = true; + do { + path++; + } while (isPathDelimiter(*path)); + } + } + + while (*path != '\0') { + const char *start = path; + while (*path != '\0' && !isPathDelimiter(*path)) + path++; + + name = uio_memdup0(path, path - start); + *endResult = uio_PathComp_new(name, path - start, last); + last = *endResult; + endResult = &last->next; + + while (isPathDelimiter(*path)) + path++; + } + + *endResult = NULL; + *pathComp = result; + if (isAbsolute != NULL) + *isAbsolute = absolute; + return 0; +} + +// Pre: pathComp forms a valid path for the platform. +void +composePath(const uio_PathComp *pathComp, uio_bool absolute, + char **path, size_t *pathLen) { + size_t len; + const uio_PathComp *ptr; + char *result; + char *pathPtr; + + assert(pathComp != NULL); + + // First determine how much space is required. + len = 0; + if (absolute) + len++; + ptr = pathComp; + while (ptr != NULL) { + len += ptr->nameLen; + ptr = ptr->next; + } + + // Allocate the required space. + result = (char *) uio_malloc(len + 1); + + // Fill the path. + pathPtr = result; + ptr = pathComp; + if (absolute) { +#ifdef HAVE_UNC_PATHS + if (ptr->name[0] == '\\') { + // UNC path + assert(ptr->name[1] == '\\'); + // Nothing to do. + } else +#endif /* HAVE_UNC_PATHS */ +#ifdef HAVE_DRIVE_LETTERS + if (ptr->nameLen == 2 && ptr->name[1] == ':' + && isDriveLetter(ptr->name[0])) { + // Nothing to do. + } + else +#endif /* HAVE_DRIVE_LETTERS */ + { + *(pathPtr++) = '/'; + } + } + + for (;;) { + memcpy(pathPtr, ptr->name, ptr->nameLen); + pathPtr += ptr->nameLen; + + ptr = ptr->next; + if (ptr == NULL) + break; + + *(pathPtr++) = '/'; + } + + *path = result; + *pathLen = len; +} + + +// *** uio_PathComp *** // + +static inline uio_PathComp * +uio_PathComp_alloc(void) { + uio_PathComp *result = uio_malloc(sizeof (uio_PathComp)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_PathComp, (void *) result); +#endif + return result; +} + +static inline void +uio_PathComp_free(uio_PathComp *pathComp) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_PathComp, (void *) pathComp); +#endif + uio_free(pathComp); +} + +// 'name' should be a null terminated string. It is stored in the PathComp, +// no copy is made. +// 'namelen' should be the length of 'name' +uio_PathComp * +uio_PathComp_new(char *name, size_t nameLen, uio_PathComp *upComp) { + uio_PathComp *result; + + result = uio_PathComp_alloc(); + result->name = name; + result->nameLen = nameLen; + result->up = upComp; + return result; +} + +void +uio_PathComp_delete(uio_PathComp *pathComp) { + uio_PathComp *next; + + while (pathComp != NULL) { + next = pathComp->next; + uio_free(pathComp->name); + uio_PathComp_free(pathComp); + pathComp = next; + } +} + +// Count the number of path components that 'comp' leads to. +int +uio_countPathComps(const uio_PathComp *comp) { + int count; + + count = 0; + for (; comp != NULL; comp = comp->next) + count++; + return count; +} + +uio_PathComp * +uio_lastPathComp(uio_PathComp *comp) { + if (comp == NULL) + return NULL; + + while (comp->next != NULL) + comp = comp->next; + return comp; +} + +// make a list of uio_PathComps from a path string +uio_PathComp * +uio_makePathComps(const char *path, uio_PathComp *upComp) { + const char *start, *end; + char *str; + uio_PathComp *result; + uio_PathComp **compPtr; // Where to put the next PathComp + + compPtr = &result; + getFirstPath0Component(path, &start, &end); + while (*start != '\0') { + str = uio_malloc(end - start + 1); + memcpy(str, start, end - start); + str[end - start] = '\0'; + + *compPtr = uio_PathComp_new(str, end - start, upComp); + upComp = *compPtr; + compPtr = &(*compPtr)->next; + getNextPath0Component(&start, &end); + } + *compPtr = NULL; + return result; +} + +void +uio_printPathComp(FILE *outStream, const uio_PathComp *comp) { + fprintf(outStream, "%s", comp->name); +} + +void +uio_printPathToComp(FILE *outStream, const uio_PathComp *comp) { + if (comp == NULL) + return; + uio_printPathToComp(outStream, comp->up); + fprintf(outStream, "/"); + uio_printPathComp(outStream, comp); +} + + diff --git a/src/libs/uio/paths.h b/src/libs/uio/paths.h new file mode 100644 index 0000000..bb5090d --- /dev/null +++ b/src/libs/uio/paths.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_PATHS_H_ +#define LIBS_UIO_PATHS_H_ + +typedef struct uio_PathComp uio_PathComp; + +#include "types.h" +#include "uioport.h" + +#include <stdio.h> + +struct uio_PathComp { + char *name; + // The name of this path component, 0-terminated + size_t nameLen; + // The length of the 'name' field, for fast lookups. + struct uio_PathComp *next; + // Next component in the path. + struct uio_PathComp *up; + // Previous component in the path. +}; + +void getFirstPathComponent(const char *dir, const char *dirEnd, + const char **startComp, const char **endComp); +void getFirstPath0Component(const char *dir, const char **startComp, + const char **endComp); +void getNextPathComponent(const char *dirEnd, + const char **startComp, const char **endComp); +void getNextPath0Component(const char **startComp, const char **endComp); +void getLastPathComponent(const char *dir, const char *dirEnd, + const char **startComp, const char **endComp); +void getLastPath0Component(const char *dir, const char **startComp, + const char **endComp); +void getPreviousPathComponent(const char *dir, const char **startComp, + const char **endComp); +#define getPreviousPath0Component getPreviousPathComponent +char *joinPaths(const char *first, const char *second); +char *joinPathsAbsolute(const char *first, const char *second); + +uio_bool validPathName(const char *path, size_t len); +size_t uio_skipUNCServerShare(const char *inPath); +size_t uio_getUNCServerShare(const char *inPath, char **outPath, + size_t *outLen); + +#ifdef HAVE_DRIVE_LETTERS +static inline int +isDriveLetter(int c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} +#endif /* HAVE_DRIVE_LETTERS */ + +static inline int +isPathDelimiter(int c) +{ +#ifdef BACKSLASH_IS_PATH_SEPARATOR + return c == '/' || c == '\\'; +#else + return c == '/'; +#endif /* BACKSLASH_IS_PATH_SEPARATOR */ +} + +int decomposePath(const char *path, uio_PathComp **pathComp, + uio_bool *isAbsolute); +void composePath(const uio_PathComp *pathComp, uio_bool absolute, + char **path, size_t *pathLen); +uio_PathComp *uio_PathComp_new(char *name, size_t nameLen, + uio_PathComp *upComp); +void uio_PathComp_delete(uio_PathComp *pathComp); +int uio_countPathComps(const uio_PathComp *comp); +uio_PathComp *uio_lastPathComp(uio_PathComp *comp); +uio_PathComp *uio_makePathComps(const char *path, uio_PathComp *upComp); +void uio_printPathComp(FILE *outStream, const uio_PathComp *comp); +void uio_printPathToComp(FILE *outStream, const uio_PathComp *comp); + +#endif /* LIBS_UIO_PATHS_H_ */ + diff --git a/src/libs/uio/physical.c b/src/libs/uio/physical.c new file mode 100644 index 0000000..18f96d1 --- /dev/null +++ b/src/libs/uio/physical.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include "physical.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif +#include "uioport.h" + +static inline uio_PRoot *uio_PRoot_alloc(void); +static inline void uio_PRoot_free(uio_PRoot *pRoot); + +// NB: ref counter is not incremented +uio_PDirHandle * +uio_PRoot_getRootDirHandle(uio_PRoot *pRoot) { + return pRoot->rootDir; +} + +void +uio_PRoot_deletePRootExtra(uio_PRoot *pRoot) { + if (pRoot->extra == NULL) + return; + assert(pRoot->handler->deletePRootExtra != NULL); + pRoot->handler->deletePRootExtra(pRoot->extra); +} + +// note: sets refMount count to 1 +// set handlerRef count to 0 +uio_PRoot * +uio_PRoot_new(uio_PDirHandle *topDirHandle, + uio_FileSystemHandler *handler, uio_Handle *handle, + uio_PRootExtra extra, int flags) { + uio_PRoot *pRoot; + + pRoot = uio_PRoot_alloc(); + pRoot->mountRef = 1; + pRoot->handleRef = 0; + pRoot->rootDir = topDirHandle; + pRoot->handler = handler; + pRoot->handle = handle; + pRoot->extra = extra; + pRoot->flags = flags; +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS + pRoot->numCloseHandlers = 0; + pRoot->closeHandlers = NULL; +#endif + + return pRoot; +} + +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS +// Closehandlers code disabled. +// It was only meant for internal use, but I don't need it any more. +// Keeping it around for a while until I'm confident I won't need it in the +// future. + +void +uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *), void *arg) { + pRoot->numCloseHandlers++; + pRoot->closeHandlers = uio_realloc(pRoot->closeHandlers, + pRoot->numCloseHandlers * sizeof (uio_PRoot_CloseHandler)); + pRoot->closeHandlers[pRoot->numCloseHandlers - 1].fun = fun; + pRoot->closeHandlers[pRoot->numCloseHandlers - 1].arg = arg; +} + +void +uio_PRoot_callCloseHandlers(uio_PRoot *pRoot) { + int i; + + i = pRoot->numCloseHandlers; + while (i--) { + uio_PRoot_CloseHandler *closeHandler; + + closeHandler = &pRoot->closeHandlers[i]; + (closeHandler->fun)(closeHandler->arg); + } +} + +void +uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot) { + pRoot->numCloseHandlers = 0; + if (pRoot->closeHandlers != NULL) + uio_free(pRoot->closeHandlers); + pRoot->closeHandlers = NULL; +} +#endif + +static inline void +uio_PRoot_delete(uio_PRoot *pRoot) { +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS + uio_PRoot_callCloseHandlers(pRoot); + uio_PRoot_removeCloseHandlers(pRoot); +#endif + assert(pRoot->handler->umount != NULL); + pRoot->handler->umount(pRoot); + if (pRoot->handle) + uio_Handle_unref(pRoot->handle); + uio_PRoot_deletePRootExtra(pRoot); + uio_PRoot_free(pRoot); +} + +static inline uio_PRoot * +uio_PRoot_alloc(void) { + uio_PRoot *result = uio_malloc(sizeof (uio_PRoot)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_PRoot, (void *) result); +#endif + return result; +} + +static inline void +uio_PRoot_free(uio_PRoot *pRoot) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_PRoot, (void *) pRoot); +#endif + uio_free(pRoot); +} + +void +uio_PRoot_refHandle(uio_PRoot *pRoot) { + pRoot->handleRef++; +} + +void +uio_PRoot_unrefHandle(uio_PRoot *pRoot) { + assert(pRoot->handleRef > 0); + pRoot->handleRef--; + if (pRoot->handleRef == 0 && pRoot->mountRef == 0) + uio_PRoot_delete(pRoot); +} + +void +uio_PRoot_refMount(uio_PRoot *pRoot) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugRef(uio_PRoot, (void *) pRoot); +#endif + pRoot->mountRef++; +} + +void +uio_PRoot_unrefMount(uio_PRoot *pRoot) { + assert(pRoot->mountRef > 0); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugUnref(uio_PRoot, (void *) pRoot); +#endif + pRoot->mountRef--; + if (pRoot->mountRef == 0 && pRoot->handleRef == 0) + uio_PRoot_delete(pRoot); +} + + diff --git a/src/libs/uio/physical.h b/src/libs/uio/physical.h new file mode 100644 index 0000000..71bc8e6 --- /dev/null +++ b/src/libs/uio/physical.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_PHYSICAL_H_ +#define LIBS_UIO_PHYSICAL_H_ + +#ifndef uio_INTERNAL_PHYSICAL +typedef void *uio_PRootExtra; +typedef void *uio_NativeEntriesContext; +#endif + +// 'forward' declarations +typedef struct uio_PRoot uio_PRoot; +typedef struct uio_PRoot_CloseHandler uio_PRoot_CloseHandler; + + +#include "iointrn.h" +#include "uioport.h" +#include "fstypes.h" + + +/* + * Represents the root of a physical directory structure. + */ +struct uio_PRoot { + int mountRef; + /* Number of times this structure is referenced from + * mount trees. */ + int handleRef; + /* Number of file or directory handles that point inside the + * physical directory strucure of this pRoot. + */ + struct uio_PDirHandle *rootDir; + struct uio_FileSystemHandler *handler; + /* How to handle files in this PRoot tree */ + int flags; +# define uio_PRoot_NOCACHE 0x0002 + struct uio_Handle *handle; + /* The handle through which this PRoot is opened, + * this is NULL for the top PRoot */ + // TODO: move this to extra? +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS + int numCloseHandlers; + uio_PRoot_CloseHandler *closeHandlers; +#endif + uio_PRootExtra extra; + /* extra internal data for some filesystem types */ +}; + +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS +struct uio_PRoot_CloseHandler { + void (*fun)(void *); + void *arg; +}; +#endif + +void uio_PRoot_deletePRootExtra(uio_PRoot *pRoot); +uio_PRoot *uio_PRoot_new(uio_PDirHandle *topDirHandle, + uio_FileSystemHandler *handler, uio_Handle *handle, + uio_PRootExtra extra, int flags); +#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS +void uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *), + void *arg); +void uio_PRoot_callCloseHandlers(uio_PRoot *pRoot); +void uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot); +#endif +uio_PDirHandle *uio_PRoot_getRootDirHandle(uio_PRoot *pRoot); +void uio_PRoot_refHandle(uio_PRoot *pRoot); +void uio_PRoot_unrefHandle(uio_PRoot *pRoot); +void uio_PRoot_refMount(uio_PRoot *pRoot); +void uio_PRoot_unrefMount(uio_PRoot *pRoot); + +#endif /* LIBS_UIO_PHYSICAL_H_ */ + + diff --git a/src/libs/uio/stdio/Makeinfo b/src/libs/uio/stdio/Makeinfo new file mode 100644 index 0000000..2d99c66 --- /dev/null +++ b/src/libs/uio/stdio/Makeinfo @@ -0,0 +1,2 @@ +uqm_CFILES="stdio.c" +uqm_HFILES="stdio.h" diff --git a/src/libs/uio/stdio/stdio.c b/src/libs/uio/stdio/stdio.c new file mode 100644 index 0000000..a4421d1 --- /dev/null +++ b/src/libs/uio/stdio/stdio.c @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +// The GPDir structures and functions are used for caching only. + +#ifdef __svr4__ +# define _POSIX_PTHREAD_SEMANTICS + // For the POSIX variant of readdir_r() +#endif + +#include "./stdio.h" + +#ifdef WIN32 +# include <io.h> +#else +# include <sys/stat.h> +# include <unistd.h> +# include <dirent.h> +#endif +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> +#include <assert.h> +#include <fcntl.h> +#include <ctype.h> + +#include "../uioport.h" +#include "../paths.h" +#include "../mem.h" +#include "../physical.h" +#ifdef uio_MEM_DEBUG +# include "../memdebug.h" +#endif + +static inline uio_GPFile *stdio_addFile(uio_GPDir *gPDir, + const char *fileName); +static inline uio_GPDir *stdio_addDir(uio_GPDir *gPDir, const char *dirName); +static char *stdio_getPath(uio_GPDir *gPDir); +static stdio_GPDirData *stdio_GPDirData_new(char *name, char *cachedPath, + uio_GPDir *upDir); +static void stdio_GPDirData_delete(stdio_GPDirData *gPDirData); +static inline stdio_GPDirData *stdio_GPDirData_alloc(void); +static inline void stdio_GPDirData_free(stdio_GPDirData *gPDirData); +static inline stdio_EntriesIterator *stdio_EntriesIterator_alloc(void); +static inline void stdio_EntriesIterator_free( + stdio_EntriesIterator *iterator); + +uio_FileSystemHandler stdio_fileSystemHandler = { + /* .init = */ NULL, + /* .unInit = */ NULL, + /* .cleanup = */ NULL, + + /* .mount = */ stdio_mount, + /* .umount = */ uio_GPRoot_umount, + + /* .access = */ stdio_access, + /* .close = */ stdio_close, + /* .fstat = */ stdio_fstat, + /* .stat = */ stdio_stat, + /* .mkdir = */ stdio_mkdir, + /* .open = */ stdio_open, + /* .read = */ stdio_read, + /* .rename = */ stdio_rename, + /* .rmdir = */ stdio_rmdir, + /* .seek = */ stdio_seek, + /* .write = */ stdio_write, + /* .unlink = */ stdio_unlink, + + /* .openEntries = */ stdio_openEntries, + /* .readEntries = */ stdio_readEntries, + /* .closeEntries = */ stdio_closeEntries, + + /* .getPDirEntryHandle = */ stdio_getPDirEntryHandle, + /* .deletePRootExtra = */ uio_GPRoot_delete, + /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete, + /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete, +}; + +uio_GPRoot_Operations stdio_GPRootOperations = { + /* .fillGPDir = */ NULL, + /* .deleteGPRootExtra = */ NULL, + /* .deleteGPDirExtra = */ stdio_GPDirData_delete, + /* .deleteGPFileExtra = */ NULL, +}; + + +void +stdio_close(uio_Handle *handle) { + int fd; + int result; + + fd = handle->native->fd; + uio_free(handle->native); + + while (1) { + result = close(fd); + if (result == 0) + break; + if (errno != EINTR) { + fprintf(stderr, "Warning: Error while closing socket: %s\n", + strerror(errno)); + break; + } + } +} + +int +stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode) { + char *path; + int result; + + path = joinPaths(stdio_getPath(pDirHandle->extra), name); + if (path == NULL) { + // errno is set + return -1; + } + + result = access(path, mode); + if (result == -1) { + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return -1; + } + + uio_free(path); + return result; +} + +int +stdio_fstat(uio_Handle *handle, struct stat *statBuf) { + return fstat(handle->native->fd, statBuf); +} + +int +stdio_stat(uio_PDirHandle *pDirHandle, const char *name, + struct stat *statBuf) { + char *path; + int result; + + path = joinPaths(stdio_getPath(pDirHandle->extra), name); + if (path == NULL) { + // errno is set + return -1; + } + + result = stat(path, statBuf); + if (result == -1) { + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return -1; + } + + uio_free(path); + return result; +} + +uio_PDirHandle * +stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name, mode_t mode) { + char *path; + uio_GPDir *newGPDir; + + path = joinPaths(stdio_getPath(pDirHandle->extra), name); + if (path == NULL) { + // errno is set + return NULL; + } + + if (MKDIR(path, mode) == -1) { + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return NULL; + } + uio_free(path); + + newGPDir = stdio_addDir(pDirHandle->extra, name); + uio_GPDir_ref(newGPDir); + return uio_PDirHandle_new(pDirHandle->pRoot, newGPDir); +} + +/* + * Function name: stdio_open + * Description: open a file from a normal stdio environment + * Arguments: gPDir - the dir where to open the file + * file - the name of the file to open + * flags - flags, as to stdio open() + * mode - mode, as to stdio open() + * Returns: handle, for use in functions accessing the opened file. + * If failed, errno is set and handle is -1. + */ +uio_Handle * +stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags, + mode_t mode) { + stdio_Handle *handle; + char *path; + int fd; + + path = joinPaths(stdio_getPath(pDirHandle->extra), file); + if (path == NULL) { + // errno is set + return NULL; + } + + fd = open(path, flags, mode); + if (fd == -1) { + int save_errno; + + save_errno = errno; + uio_free(path); + errno = save_errno; + return NULL; + } + uio_free(path); + +#if 0 + if (flags & O_CREAT) { + if (uio_GPDir_getGPDirEntry(pDirHandle->extra, file) == NULL) + stdio_addFile(pDirHandle->extra, file); + } +#endif + + handle = uio_malloc(sizeof (stdio_Handle)); + handle->fd = fd; + + return uio_Handle_new(pDirHandle->pRoot, handle, flags); +} + +ssize_t +stdio_read(uio_Handle *handle, void *buf, size_t count) { + return read(handle->native->fd, buf, count); +} + +int +stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName, + uio_PDirHandle *newPDirHandle, const char *newName) { + char *newPath, *oldPath; + int result; + + oldPath = joinPaths(stdio_getPath(oldPDirHandle->extra), oldName); + if (oldPath == NULL) { + // errno is set + return -1; + } + + newPath = joinPaths(stdio_getPath(newPDirHandle->extra), newName); + if (newPath == NULL) { + // errno is set + uio_free(oldPath); + return -1; + } + + result = rename(oldPath, newPath); + if (result == -1) { + int savedErrno = errno; + uio_free(oldPath); + uio_free(newPath); + errno = savedErrno; + return -1; + } + + uio_free(oldPath); + uio_free(newPath); + + { + // update the GPDir structure + uio_GPDirEntry *entry; + + // TODO: add locking + entry = uio_GPDir_getGPDirEntry(oldPDirHandle->extra, oldName); + if (entry != NULL) { + uio_GPDirEntries_remove(oldPDirHandle->extra->entries, oldName); + uio_GPDirEntries_add(newPDirHandle->extra->entries, newName, + entry); + } + } + + return result; +} + +int +stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name) { + char *path; + int result; + + path = joinPaths(stdio_getPath(pDirHandle->extra), name); + if (path == NULL) { + // errno is set + return -1; + } + + result = rmdir(path); + if (result == -1) { + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return -1; + } + + uio_free(path); + + uio_GPDir_removeSubDir(pDirHandle->extra, name); + + return result; +} + +off_t +stdio_seek(uio_Handle *handle, off_t offset, int whence) { + return lseek(handle->native->fd, offset, whence); +} + +ssize_t +stdio_write(uio_Handle *handle, const void *buf, size_t count) { + return write(handle->native->fd, buf, count); +} + +int +stdio_unlink(uio_PDirHandle *pDirHandle, const char *name) { + char *path; + int result; + + path = joinPaths(stdio_getPath(pDirHandle->extra), name); + if (path == NULL) { + // errno is set + return -1; + } + + result = unlink(path); + if (result == -1) { + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return -1; + } + + uio_free(path); + + uio_GPDir_removeFile(pDirHandle->extra, name); + + return result; +} + +uio_PDirEntryHandle * +stdio_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, const char *name) { + uio_PDirEntryHandle *result; + const char *pathUpTo; + char *path; + struct stat statBuf; +#ifdef HAVE_DRIVE_LETTERS + char driveName[3]; +#endif /* HAVE_DRIVE_LETTERS */ + +#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) + if (pDirHandle->extra->extra->upDir == NULL) { + // Top dir. Contains only drive letters and UNC \\server\share + // parts. +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(name[0]) && name[1] == ':' && name[2] == '\0') { + driveName[0] = tolower(name[0]); + driveName[1] = ':'; + driveName[2] = '\0'; + name = driveName; + } else +#endif /* HAVE_DRIVE_LETTERS */ +#ifdef HAVE_UNC_PATHS + { + size_t uncLen; + + uncLen = uio_skipUNCServerShare(name); + if (name[uncLen] != '\0') { + // 'name' contains neither a drive letter, nor the + // first part of a UNC path. + return NULL; + } + } +#else /* !defined(HAVE_UNC_PATHS) */ + { + // Make sure that there is an 'else' case if HAVE_DRIVE_LETTERS + // is defined. + } +#endif /* HAVE_UNC_PATHS */ + } +#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */ + + result = uio_GPDir_getPDirEntryHandle(pDirHandle, name); + if (result != NULL) + return result; + +#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) + if (pDirHandle->extra->extra->upDir == NULL) { + // Need to create a 'directory' for the drive letter or UNC + // "\\server\share" part. + // It's no problem if we happen to create a dir for a non-existing + // drive. It should just produce an empty dir. + uio_GPDir *gPDir; + + gPDir = stdio_addDir(pDirHandle->extra, name); + uio_GPDir_ref(gPDir); + return (uio_PDirEntryHandle *) uio_PDirHandle_new( + pDirHandle->pRoot, gPDir); + } +#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */ + + pathUpTo = stdio_getPath(pDirHandle->extra); + if (pathUpTo == NULL) { + // errno is set + return NULL; + } + path = joinPaths(pathUpTo, name); + if (path == NULL) { + // errno is set + return NULL; + } + + if (stat(path, &statBuf) == -1) { +#ifdef __SYMBIAN32__ + // XXX: HACK: If we don't have access to a directory, we can still + // have access to the underlying entries. We don't actually know + // whether the entry is a directory, but I know of no way to find + // out. We just pretend that it is; worst case, a file which we can't + // access shows up as a directory which we can't access. + if (errno == EACCES) { + statBuf.st_mode = S_IFDIR; + // Fake a directory; the other fields of the stat + // structure are unused. + } else +#endif + { + // errno is set. + int savedErrno = errno; + uio_free(path); + errno = savedErrno; + return NULL; + } + } + uio_free(path); + + if (S_ISREG(statBuf.st_mode)) { + uio_GPFile *gPFile; + + gPFile = stdio_addFile(pDirHandle->extra, name); + uio_GPFile_ref(gPFile); + return (uio_PDirEntryHandle *) uio_PFileHandle_new( + pDirHandle->pRoot, gPFile); + } else if (S_ISDIR(statBuf.st_mode)) { + uio_GPDir *gPDir; + + gPDir = stdio_addDir(pDirHandle->extra, name); + uio_GPDir_ref(gPDir); + return (uio_PDirEntryHandle *) uio_PDirHandle_new( + pDirHandle->pRoot, gPDir); + } else { +#ifdef DEBUG + fprintf(stderr, "Warning: Attempt to access '%s' from '%s', " + "which is not a regular file, nor a directory.\n", name, + pathUpTo); +#endif + return NULL; + } +} + +uio_PRoot * +stdio_mount(uio_Handle *handle, int flags) { + uio_PRoot *result; + stdio_GPDirData *extra; + + assert (handle == NULL); + extra = stdio_GPDirData_new( + uio_strdup("") /* name */, +#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) + // Full paths start with a drive letter or \\server\share + uio_strdup("") /* cached path */, +#else + uio_strdup("/") /* cached path */, +#endif /* HAVE_DRIVE_LETTERS */ + NULL /* parent dir */); + + result = uio_GPRoot_makePRoot( + uio_getFileSystemHandler(uio_FSTYPE_STDIO), flags, + &stdio_GPRootOperations, NULL, uio_GPRoot_PERSISTENT, + handle, extra, 0); + + uio_GPDir_setComplete(result->rootDir->extra, true); + + return result; +} + +#ifdef WIN32 +stdio_EntriesIterator * +stdio_openEntries(uio_PDirHandle *pDirHandle) { + const char *dirPath; + char path[PATH_MAX]; + char *pathEnd; + size_t dirPathLen; + stdio_EntriesIterator *iterator; + +// uio_GPDir_access(pDirHandle->extra); + + dirPath = stdio_getPath(pDirHandle->extra); + if (dirPath == NULL) { + // errno is set + return NULL; + } + + dirPathLen = strlen(dirPath); + if (dirPathLen > PATH_MAX - 3) { + // dirPath ++ '/' ++ '*' ++ '\0' + errno = ENAMETOOLONG; + return NULL; + } + memcpy(path, dirPath, dirPathLen); + pathEnd = path + dirPathLen; + pathEnd[0] = '/'; + pathEnd[1] = '*'; + pathEnd[2] = '\0'; + iterator = stdio_EntriesIterator_new(0); + iterator->dirHandle = _findfirst(path, &iterator->findData); + if (iterator->dirHandle == 1) { + if (errno != ENOENT) { + stdio_EntriesIterator_delete(iterator); + return NULL; + } + iterator->status = 1; + } else + iterator->status = 0; + return iterator; +} +#endif + +#ifndef WIN32 +stdio_EntriesIterator * +stdio_openEntries(uio_PDirHandle *pDirHandle) { + const char *dirPath; + DIR *dirHandle; + stdio_EntriesIterator *result; + +// uio_GPDir_access(pDirHandle->extra); + + dirPath = stdio_getPath(pDirHandle->extra); + if (dirPath == NULL) { + // errno is set + return NULL; + } + + dirHandle = opendir(dirPath); + if (dirHandle == NULL) { + // errno is set; + return NULL; + } + + result = stdio_EntriesIterator_new(dirHandle); + result->status = readdir_r(dirHandle, result->direntBuffer, + &result->entry); +#ifndef WIN32 +# ifdef DEBUG + if (result->status != 0) { + fprintf(stderr, "Warning: readdir_r() failed: %s\n", + strerror(result->status)); + } +# endif +#endif + return result; +} +#endif + +// the start of 'buf' will be filled with pointers to strings +// those strings are stored elsewhere in buf. +// The function returns the number of strings passed along, or -1 for error. +// If there are no more entries, the last pointer will be NULL. +// (this pointer counts towards the return value) +int +stdio_readEntries(stdio_EntriesIterator **iteratorPtr, + char *buf, size_t len) { + char *end; + char **start; + int num; + const char *name; + size_t nameLen; + stdio_EntriesIterator *iterator; + + iterator = *iteratorPtr; + + // buf will be filled like this: + // The start of buf will contain pointers to char *, + // the end will contain the actual char[] that those pointers point to. + // The start and the end will grow towards eachother. + start = (char **) buf; + end = buf + len; + num = 0; +#ifdef WIN32 + for (; iterator->status == 0; + iterator->status = _findnext(iterator->dirHandle, + &iterator->findData)) +#else + for (; iterator->status == 0 && iterator->entry != NULL; + iterator->status = readdir_r(iterator->dirHandle, + iterator->direntBuffer, &iterator->entry)) +#endif + { +#ifdef WIN32 + name = iterator->findData.name; +#else + name = iterator->entry->d_name; +#endif + if (name[0] == '.' && + (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0'))) { + // skip directories "." and ".." + continue; + } + nameLen = strlen(name) + 1; + + // Does this work with systems that need memory access to be + // aligned on a certain number of bytes? + if ((size_t) (sizeof (char *) + nameLen) > + (size_t) (end - (char *) start)) { + // Not enough room to fit the pointer (at the beginning) and + // the string (at the end). + return num; + } + end -= nameLen; + memcpy(end, name, nameLen); + *start = end; + start++; + num++; + } +#ifndef WIN32 +# ifdef DEBUG + if (iterator->status != 0) { + fprintf(stderr, "Warning: readdir_r() failed: %s\n", + strerror(iterator->status)); + } +# endif +#endif + if (sizeof (char *) > (size_t) (end - (char *) start)) { + // not enough room to fit the NULL pointer. + // It will have to be reported seperately the next time. + return num; + } + *start = NULL; + num++; + return num; +} + +void +stdio_closeEntries(stdio_EntriesIterator *iterator) { +#ifdef WIN32 + _findclose(iterator->dirHandle); +#else + closedir(iterator->dirHandle); +#endif + stdio_EntriesIterator_delete(iterator); +} + +#ifdef WIN32 +stdio_EntriesIterator * +stdio_EntriesIterator_new(long dirHandle) { + stdio_EntriesIterator *result; + + result = stdio_EntriesIterator_alloc(); + result->dirHandle = dirHandle; + return result; +} +#else +stdio_EntriesIterator * +stdio_EntriesIterator_new(DIR *dirHandle) { + stdio_EntriesIterator *result; + size_t bufferSize; + + result = stdio_EntriesIterator_alloc(); + result->dirHandle = dirHandle; + + // Linux's and FreeBSD's struct dirent are defined with a + // maximum d_name field (NAME_MAX). + // However, POSIX doesn't require this, and in fact + // at least QNX defines struct dirent with an empty d_name field. + // Solaris defineds it with a d_name field of length 1. + // This should take care of it: + bufferSize = sizeof (struct dirent) + - sizeof (((struct dirent *) 0)->d_name) + (NAME_MAX + 1); + // Take the length of the dirent structure as it is defined, + // subtract the length of the d_name field, and add the length + // of the maximum length d_name field (NAME_MAX plus 1 for + // the '\0'). + // XXX: Could this give problems with weird alignments? + result->direntBuffer = uio_malloc(bufferSize); + return result; +} +#endif + +void +stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator) { +#ifndef WIN32 + uio_free(iterator->direntBuffer); +#endif + stdio_EntriesIterator_free(iterator); +} + +static inline stdio_EntriesIterator * +stdio_EntriesIterator_alloc(void) { + return uio_malloc(sizeof (stdio_EntriesIterator)); +} + +static inline void +stdio_EntriesIterator_free(stdio_EntriesIterator *iterator) { + uio_free(iterator); +} + +static inline uio_GPFile * +stdio_addFile(uio_GPDir *gPDir, const char *fileName) { + uio_GPFile *file; + + file = uio_GPFile_new(gPDir->pRoot, NULL, + uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags)); + uio_GPDir_addFile(gPDir, fileName, file); + return file; +} + +// called by fillGPDir when a subdir is found +static inline uio_GPDir * +stdio_addDir(uio_GPDir *gPDir, const char *dirName) { + uio_GPDir *subDir; + + subDir = uio_GPDir_prepareSubDir(gPDir, dirName); + if (subDir->extra == NULL) { + // It's a new dir, we'll need to add our own data. + uio_GPDir_ref(gPDir); + subDir->extra = stdio_GPDirData_new(uio_strdup(dirName), + NULL, gPDir); + uio_GPDir_setComplete(subDir, true); + // fillPDir should not be called. + } + uio_GPDir_commitSubDir(gPDir, dirName, subDir); + return subDir; +} + +// returns a pointer to gPDir->extra->cachedPath +// pointer should not be stored, the memory it points to can be freed +// lateron. TODO: not threadsafe. +static char * +stdio_getPath(uio_GPDir *gPDir) { + if (gPDir->extra->cachedPath == NULL) { + char *upPath; + size_t upPathLen, nameLen; + + if (gPDir->extra->upDir == NULL) { +#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) + // Drive letter or UNC \\server\share still needs to follow. + gPDir->extra->cachedPath = uio_malloc(1); + gPDir->extra->cachedPath[0] = '\0'; +#else + gPDir->extra->cachedPath = uio_malloc(2); + gPDir->extra->cachedPath[0] = '/'; + gPDir->extra->cachedPath[1] = '\0'; +#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */ + return gPDir->extra->cachedPath; + } + + upPath = stdio_getPath(gPDir->extra->upDir); + if (upPath == NULL) { + // errno is set + return NULL; + } + +#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) + if (upPath[0] == '\0') { + // The up dir is the root dir. Directly below the root dir are + // only dirs for drive letters and UNC \\share\server parts. + // No '/' needs to be attached. + gPDir->extra->cachedPath = uio_strdup(gPDir->extra->name); + return gPDir->extra->cachedPath; + } +#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */ + upPathLen = strlen(upPath); +#if !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS) + if (upPath[upPathLen - 1] == '/') { + // should only happen for "/" + upPathLen--; + } +#endif /* !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS) */ + nameLen = strlen(gPDir->extra->name); + if (upPathLen + nameLen + 1 >= PATH_MAX) { + errno = ENAMETOOLONG; + return NULL; + } + gPDir->extra->cachedPath = uio_malloc(upPathLen + nameLen + 2); + memcpy(gPDir->extra->cachedPath, upPath, upPathLen); + gPDir->extra->cachedPath[upPathLen] = '/'; + memcpy(gPDir->extra->cachedPath + upPathLen + 1, + gPDir->extra->name, nameLen); + gPDir->extra->cachedPath[upPathLen + nameLen + 1] = '\0'; + } + return gPDir->extra->cachedPath; +} + +static stdio_GPDirData * +stdio_GPDirData_new(char *name, char *cachedPath, uio_GPDir *upDir) { + stdio_GPDirData *result; + + result = stdio_GPDirData_alloc(); + result->name = name; + result->cachedPath = cachedPath; + result->upDir = upDir; + return result; +} + +static void +stdio_GPDirData_delete(stdio_GPDirData *gPDirData) { + if (gPDirData->upDir != NULL) + uio_GPDir_unref(gPDirData->upDir); + stdio_GPDirData_free(gPDirData); +} + +static inline stdio_GPDirData * +stdio_GPDirData_alloc(void) { + stdio_GPDirData *result; + + result = uio_malloc(sizeof (stdio_GPDirData)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(stdio_GPDirData, (void *) result); +#endif + return result; +} + +static inline void +stdio_GPDirData_free(stdio_GPDirData *gPDirData) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(stdio_GPDirData, (void *) gPDirData); +#endif + uio_free(gPDirData->name); + if (gPDirData->cachedPath != NULL) + uio_free(gPDirData->cachedPath); + uio_free(gPDirData); +} + + diff --git a/src/libs/uio/stdio/stdio.h b/src/libs/uio/stdio/stdio.h new file mode 100644 index 0000000..914a1d7 --- /dev/null +++ b/src/libs/uio/stdio/stdio.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +typedef struct stdio_Handle *uio_NativeHandle; +typedef void *uio_GPRootExtra; +typedef struct stdio_GPDirData *uio_GPDirExtra; +typedef void *uio_GPFileExtra; +typedef struct stdio_EntriesIterator stdio_EntriesIterator; +typedef stdio_EntriesIterator *uio_NativeEntriesContext; + + +#define uio_INTERNAL_PHYSICAL + +#include "../gphys.h" +#include "../iointrn.h" +#include "../uioport.h" +#include "../fstypes.h" +#include "../physical.h" + +#include <sys/stat.h> +#ifndef WIN32 +# include <dirent.h> +#endif + + +typedef struct stdio_GPDirData { + // The reason that names are stored is that in the system filesystem + // you need names to refer to files and directories. + // (you could keep a file descriptor to each one, but that would + // mean a lot of open file descriptors, and for some it won't even + // be enough). + // This is not needed for all filesystems; therefor this info is not + // in uio_GPDir itself. + // The reasons for including upDir here are similar. + char *name; + char *cachedPath; + uio_GPDir *upDir; +} stdio_GPDirData; + +typedef struct stdio_Handle { + int fd; +} stdio_Handle; + +#ifdef WIN32 +struct stdio_EntriesIterator { + long dirHandle; + struct _finddata_t findData; + int status; +}; +#endif + +#ifndef WIN32 +struct stdio_EntriesIterator { + DIR *dirHandle; + struct dirent *entry; + struct dirent *direntBuffer; + int status; +}; +#endif + + +uio_PRoot *stdio_mount(uio_Handle *handle, int flags); +int stdio_umount(uio_PRoot *); +uio_PDirHandle *stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name, + mode_t mode); +uio_Handle *stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags, + mode_t mode); +void stdio_close(uio_Handle *handle); +int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode); +int stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode); +int stdio_fstat(uio_Handle *handle, struct stat *statBuf); +int stdio_stat(uio_PDirHandle *pDirHandle, const char *name, + struct stat *statBuf); +ssize_t stdio_read(uio_Handle *handle, void *buf, size_t count); +int stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName, + uio_PDirHandle *newPDirHandle, const char *newName); +int stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name); +off_t stdio_seek(uio_Handle *handle, off_t offset, int whence); +ssize_t stdio_write(uio_Handle *handle, const void *buf, size_t count); +int stdio_unlink(uio_PDirHandle *pDirHandle, const char *name); + +stdio_EntriesIterator *stdio_openEntries(uio_PDirHandle *pDirHandle); +int stdio_readEntries(stdio_EntriesIterator **iterator, + char *buf, size_t len); +void stdio_closeEntries(stdio_EntriesIterator *iterator); +#ifdef WIN32 +stdio_EntriesIterator *stdio_EntriesIterator_new(long dirHandle); +#else +stdio_EntriesIterator *stdio_EntriesIterator_new(DIR *dirHandle); +#endif +void stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator); +uio_PDirEntryHandle *stdio_getPDirEntryHandle( + const uio_PDirHandle *pDirHandle, const char *name); + diff --git a/src/libs/uio/types.h b/src/libs/uio/types.h new file mode 100644 index 0000000..b92f7a4 --- /dev/null +++ b/src/libs/uio/types.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 _uio_TYPES_H +#define _uio_TYPES_H + +#include "config.h" + +// ISO C99 compatible boolean types. The ISO C99 standard defines: +// - An object declared as type _Bool, large enough to store the values 0 +// and 1, the rank of which is less than the rank of all other standard +// integer types. +// - A macro "bool", which expands to "_Bool". +// - A macro "true", which expands to the integer constant 1, suitable for +// use in #if preprocessing directives. +// - A macro "false", which expands to the integer constant 0, suitable for +// use in #if preprocessing directives. +// - A macro "__bool_true_false_are_defined", which expands to the integer +// constant 1, suitable for use in #if preprocessing directives. +#ifndef __bool_true_false_are_defined +#undef bool +#undef false +#undef true +#ifndef HAVE__BOOL +typedef unsigned char _Bool; +#endif /* HAVE_BOOL */ +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined +#endif /* __bool_true_false_are_defined */ + +typedef bool uio_bool; + +typedef unsigned char uio_uint8; +typedef signed char uio_sint8; +typedef unsigned short uio_uint16; +typedef signed short uio_sint16; +typedef unsigned int uio_uint32; +typedef signed int uio_sint32; + +typedef unsigned long uio_uintptr; + // Needs to be adapted for 64 bits systems + +#endif /* _uio_TYPES_H */ + + diff --git a/src/libs/uio/uioport.h b/src/libs/uio/uioport.h new file mode 100644 index 0000000..69d7e8e --- /dev/null +++ b/src/libs/uio/uioport.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_UIOPORT_H_ +#define LIBS_UIO_UIOPORT_H_ + +#ifdef _MSC_VER +# include <io.h> +#else +# include <unistd.h> +#endif + + +// Compilation related +#ifndef inline +# ifdef _MSC_VER +# define inline __inline +# else +# define inline __inline__ +# endif +#endif + +// Paths +#ifdef WIN32 +# include <stdlib.h> +# define PATH_MAX _MAX_PATH +# define NAME_MAX _MAX_FNAME + // _MAX_DIR and FILENAME_MAX could also be candidates. + // If anyone can tell me which one matches NAME_MAX, please + // let me know. +#elif defined(_WIN32_WCE) +# include <sys/syslimits.h> +#else +# include <limits.h> + /* PATH_MAX is per POSIX defined in <limits.h>, but: + * "A definition of one of the values from Table 2.6 shall bea + * omitted from <limits.h> on specific implementations where the + * corresponding value is equal to or greater than the + * stated minimum, but where the value can vary depending + * on the file to which it is applied. The actual value supported + * for a specific pathname shall be provided by the pathconf() + * function." + * _POSIX_NAME_MAX will provide a minimum (14). + * This is relevant (at least) for Solaris. + */ +# ifndef NAME_MAX +# define NAME_MAX _POSIX_NAME_MAX +# endif +#endif + +// Variations in path handling +#if defined(WIN32) || defined(__SYMBIAN32__) + // HAVE_DRIVE_LETTERS is defined to signify that DOS/Windows style drive + // letters are to be recognised on this platform. +# define HAVE_DRIVE_LETTERS + // BACKSLASH_IS_PATH_SEPARATOR is defined to signify that the backslash + // character is to be recognised as a path separator on this platform. + // This does not affect the acceptance of forward slashes as path + // separators. +# define BACKSLASH_IS_PATH_SEPARATOR +#endif +#if defined(WIN32) + // HAVE_UNC_PATHS is defined to signify that Universal Naming Convention + // style paths are to be recognised on this platform. +# define HAVE_UNC_PATHS +#endif + +// User ids +#ifdef WIN32 +typedef short uid_t; +typedef short gid_t; +#endif + +// Some types +#ifdef _MSC_VER +typedef int ssize_t; +typedef unsigned short mode_t; +#endif + +// Directories +#include <sys/stat.h> +#ifdef WIN32 +# ifdef _MSC_VER +# define MKDIR(name, mode) ((void) mode, _mkdir(name)) +# else +# define MKDIR(name, mode) ((void) mode, mkdir(name)) +# endif +#else +# define MKDIR mkdir +#endif +#ifdef _MSC_VER +# include <direct.h> +# define chdir _chdir +# define getcwd _getcwd +# define chdir _chdir +# define getcwd _getcwd +# define access _access +# define F_OK 0 +# define W_OK 2 +# define R_OK 4 +# define open _open +# define read _read +# define rmdir _rmdir +# define lseek _lseek +# define lstat _lstat +# define fstat _fstat +# define S_IRUSR S_IREAD +# define S_IWUSR S_IWRITE +# define S_IXUSR S_IEXEC +# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +# define S_IRGRP 0 +# define S_IWGRP 0 +# define S_IXGRP 0 +# define S_IROTH 0 +# define S_IWOTH 0 +# define S_IXOTH 0 +# define S_IRWXG 0 +# define S_IRWXO 0 +# define S_ISUID 0 +# define S_ISGID 0 +# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) +# define S_IFMT _S_IFMT +# define S_IFREG _S_IFREG +# define S_IFCHR _S_IFCHR +# define S_IFDIR _S_IFDIR +# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR) +# define S_ISREG(mode) (((mode) & _S_IFMT) == _S_IFREG) +# define write _write +# define stat _stat +# define unlink _unlink +#elif defined (__MINGW32__) +# define S_IRGRP 0 +# define S_IWGRP 0 +# define S_IXGRP 0 +# define S_IROTH 0 +# define S_IWOTH 0 +# define S_IXOTH 0 +# define S_IRWXG 0 +# define S_IRWXO 0 +# define S_ISUID 0 +# define S_ISGID 0 +# define S_IFMT _S_IFMT +# define S_IFREG _S_IFREG +# define S_IFCHR _S_IFCHR +# define S_IFDIR _S_IFDIR +#endif +#ifdef __SYMBIAN32__ + // TODO: Symbian doesn't have readdir_r(). If uio is to be usable + // outside of uqm (which defines its own backup readdir_r()), an + // implementation of that function needs to be added to uio. +# include <dirent.h> + int readdir_r (DIR *dirp, struct dirent *entry, struct dirent **result); +#endif + +#endif /* LIBS_UIO_UIOPORT_H_ */ + diff --git a/src/libs/uio/uiostream.c b/src/libs/uio/uiostream.c new file mode 100644 index 0000000..eb101a5 --- /dev/null +++ b/src/libs/uio/uiostream.c @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 "uioport.h" +#include "iointrn.h" +#include "uiostream.h" + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> + +#include "uioutils.h" +#include "utils.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +#define uio_Stream_BLOCK_SIZE 1024 + +static inline uio_Stream *uio_Stream_new(uio_Handle *handle, int openFlags); +static inline void uio_Stream_delete(uio_Stream *stream); +static inline uio_Stream *uio_Stream_alloc(void); +static inline void uio_Stream_free(uio_Stream *stream); +#ifdef NDEBUG +# define uio_assertReadSanity(stream) +# define uio_assertWriteSanity(stream) +#else +static void uio_assertReadSanity(uio_Stream *stream); +static void uio_assertWriteSanity(uio_Stream *stream); +#endif +static int uio_Stream_fillReadBuffer(uio_Stream *stream); +static int uio_Stream_flushWriteBuffer(uio_Stream *stream); +static void uio_Stream_discardReadBuffer(uio_Stream *stream); + + +uio_Stream * +uio_fopen(uio_DirHandle *dir, const char *path, const char *mode) { + int openFlags; + uio_Handle *handle; + uio_Stream *stream; + int i; + + switch (*mode) { + case 'r': + openFlags = O_RDONLY; + break; + case 'w': + openFlags = O_WRONLY | O_CREAT | O_TRUNC; + break; + case 'a': + openFlags = O_WRONLY| O_CREAT | O_APPEND; + default: + errno = EINVAL; + fprintf(stderr, "Invalid mode string in call to uio_fopen().\n"); + return NULL; + } + mode++; + + // C'89 says 'b' may either be the second or the third character. + // If someone specifies both 'b' and 't', he/she is out of luck. + i = 2; + while (i-- && (*mode != '\0')) { + switch (*mode) { + case 'b': +#ifdef WIN32 + openFlags |= O_BINARY; +#endif + break; + case 't': +#ifdef WIN32 + openFlags |= O_TEXT; +#endif + break; + case '+': + openFlags = (openFlags & ~O_ACCMODE) | O_RDWR; + break; + default: + i = 0; + // leave the while loop + break; + } + mode++; + } + + // Any characters in the mode string that might follow are ignored. + + handle = uio_open(dir, path, openFlags, S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (handle == NULL) { + // errno is set + return NULL; + } + + stream = uio_Stream_new(handle, openFlags); + return stream; +} + +int +uio_fclose(uio_Stream *stream) { + if (stream->operation == uio_StreamOperation_write) + uio_Stream_flushWriteBuffer(stream); + uio_close(stream->handle); + uio_Stream_delete(stream); + return 0; +} + +// "The file position indicator for the stream (if defined) is advanced by +// the number of characters successfully read. If an error occurs, the +// resulting value of the file position indicator for the stream is +// indeterminate. If a partial element is read, its value is +// indeterminate." (from POSIX for fread()). +size_t +uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) { + size_t bytesToRead; + size_t bytesRead; + + bytesToRead = size * nmemb; + bytesRead = 0; + + uio_assertReadSanity(stream); + stream->operation = uio_StreamOperation_read; + + if (stream->dataEnd > stream->dataStart) { + // First use what's in the buffer. + size_t numRead; + + numRead = minu(stream->dataEnd - stream->dataStart, bytesToRead); + memcpy(buf, stream->dataStart, numRead); + buf = (void *) ((char *) buf + numRead); + stream->dataStart += numRead; + bytesToRead -= numRead; + bytesRead += numRead; + } + if (bytesToRead == 0) { + // Done already + return nmemb; + } + + { + // Read the rest directly into the caller's buffer. + ssize_t numRead; + numRead = uio_read(stream->handle, buf, bytesToRead); + if (numRead == -1) { + stream->status = uio_Stream_STATUS_ERROR; + goto out; + } + bytesRead += numRead; + if ((size_t) numRead < bytesToRead) { + // End of file + stream->status = uio_Stream_STATUS_EOF; + stream->operation = uio_StreamOperation_none; + goto out; + } + } + +out: + if (bytesToRead == 0) + return nmemb; + return bytesRead / size; +} + +char * +uio_fgets(char *s, int size, uio_Stream *stream) { + int orgSize; + char *buf; + + uio_assertReadSanity(stream); + stream->operation = uio_StreamOperation_read; + + size--; + orgSize = size; + buf = s; + while (size > 0) { + size_t maxRead; + const char *newLinePos; + + // Fill buffer if empty. + if (stream->dataStart == stream->dataEnd) { + if (uio_Stream_fillReadBuffer(stream) == -1) { + // errno is set + stream->status = uio_Stream_STATUS_ERROR; + return NULL; + } + if (stream->dataStart == stream->dataEnd) { + // End-of-file + stream->status = uio_Stream_STATUS_EOF; + stream->operation = uio_StreamOperation_none; + if (size == orgSize) { + // Nothing was read. + return NULL; + } + break; + } + } + + // Search in buffer + maxRead = minu(stream->dataEnd - stream->dataStart, size); + newLinePos = memchr(stream->dataStart, '\n', maxRead); + if (newLinePos != NULL) { + // Newline found. + maxRead = newLinePos + 1 - stream->dataStart; + memcpy(buf, stream->dataStart, maxRead); + stream->dataStart += maxRead; + buf[maxRead] = '\0'; + return buf; + } + // No newline present. + memcpy(buf, stream->dataStart, maxRead); + stream->dataStart += maxRead; + buf += maxRead; + size -= maxRead; + } + + *buf = '\0'; + return s; +} + +int +uio_fgetc(uio_Stream *stream) { + int result; + + uio_assertReadSanity(stream); + stream->operation = uio_StreamOperation_read; + + if (stream->dataStart == stream->dataEnd) { + // Buffer is empty + if (uio_Stream_fillReadBuffer(stream) == -1) { + stream->status = uio_Stream_STATUS_ERROR; + return (int) EOF; + } + if (stream->dataStart == stream->dataEnd) { + // End-of-file + stream->status = uio_Stream_STATUS_EOF; + stream->operation = uio_StreamOperation_none; + return (int) EOF; + } + } + + result = (int) *((unsigned char *) stream->dataStart); + stream->dataStart++; + return result; +} + +// Only one character pushback is guaranteed, just like with stdio ungetc(). +int +uio_ungetc(int c, uio_Stream *stream) { + assert((stream->openFlags & O_ACCMODE) != O_WRONLY); + assert(c >= 0 && c <= 255); + + return (int) EOF; + // not implemented +// return c; +} + +// NB. POSIX allows errno to be set for vsprintf(), but does not require it: +// "The value of errno may be set to nonzero by a library function call +// whether or not there is an error, provided the use of errno is not +// documented in the description of the function in this International +// Standard." The latter is the case for vsprintf(). +int +uio_vfprintf(uio_Stream *stream, const char *format, va_list args) { + // This could be done faster, but going through snprintf() is easiest, + // and is fast enough for now. + char *buf; + int putResult; + int savedErrno; + + buf = uio_vasprintf(format, args); + if (buf == NULL) { + // errno may or may not be set + return -1; + } + + putResult = uio_fputs(buf, stream); + savedErrno = errno; + + uio_free(buf); + + errno = savedErrno; + return putResult; +} + +int +uio_fprintf(uio_Stream *stream, const char *format, ...) { + va_list args; + int result; + + va_start(args, format); + result = uio_vfprintf(stream, format, args); + va_end(args); + + return result; +} + +int +uio_fputc(int c, uio_Stream *stream) { + assert((stream->openFlags & O_ACCMODE) != O_RDONLY); + assert(c >= 0 && c <= 255); + + uio_assertWriteSanity(stream); + stream->operation = uio_StreamOperation_write; + + if (stream->dataEnd == stream->bufEnd) { + // The buffer is full. Flush it out. + if (uio_Stream_flushWriteBuffer(stream) == -1) { + // errno is set + // Error status (for ferror()) is set. + return EOF; + } + } + + *(unsigned char *) stream->dataEnd = (unsigned char) c; + stream->dataEnd++; + return c; +} + +int +uio_fputs(const char *s, uio_Stream *stream) { + int result; + + result = uio_fwrite(s, strlen(s), 1, stream); + if (result != 1) + return EOF; + return 0; +} + +int +uio_fseek(uio_Stream *stream, long offset, int whence) { + int newPos; + + if (stream->operation == uio_StreamOperation_read) { + uio_Stream_discardReadBuffer(stream); + } else if (stream->operation == uio_StreamOperation_write) { + if (uio_Stream_flushWriteBuffer(stream) == -1) { + // errno is set + return -1; + } + } + assert(stream->dataStart == stream->buf); + assert(stream->dataEnd == stream->buf); + stream->operation = uio_StreamOperation_none; + + newPos = uio_lseek(stream->handle, offset, whence); + if (newPos == -1) { + // errno is set + return -1; + } + stream->status = uio_Stream_STATUS_OK; + // Clear error or end-of-file flag. + + return 0; +} + +long +uio_ftell(uio_Stream *stream) { + off_t newPos; + + newPos = uio_lseek(stream->handle, 0, SEEK_CUR); + if (newPos == (off_t) -1) { + // errno is set + return (long) -1; + } + + if (stream->operation == uio_StreamOperation_write) { + newPos += stream->dataEnd - stream->dataStart; + } else if (stream->operation == uio_StreamOperation_read) { + newPos -= stream->dataEnd - stream->dataStart; + } + + return (long) newPos; +} + +// If less that nmemb elements could be written, or an error occurs, the +// file pointer is undefined. clearerr() followed by fseek() need to be +// called before attempting to read or write again. +// I don't have the C standard myself, but I suspect this is the official +// behaviour for fread() and fwrite(). +size_t +uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) { + ssize_t bytesToWrite; + ssize_t bytesWritten; + + uio_assertWriteSanity(stream); + stream->operation = uio_StreamOperation_write; + + // NB. If a file is opened in append mode, the file position indicator + // is moved to the end of the file before writing. + // We leave that up to the physical layer. + + bytesToWrite = size * nmemb; + if (bytesToWrite < stream->bufEnd - stream->dataEnd) { + // There's enough space in the write buffer to store everything. + memcpy(stream->dataEnd, buf, bytesToWrite); + stream->dataEnd += bytesToWrite; + return nmemb; + } + + // Not enough space in the write buffer to write everything. + // Flush what's left in the write buffer first. + if (uio_Stream_flushWriteBuffer(stream) == -1) { + // errno is set + // Error status (for ferror()) is set. + return 0; + } + + if (bytesToWrite < stream->bufEnd - stream->dataEnd) { + // The now empty write buffer is large enough to store everything. + memcpy(stream->dataEnd, buf, bytesToWrite); + stream->dataEnd += bytesToWrite; + return nmemb; + } + + // There is more data to write than fits in the (empty) write buffer. + // The data is written directly, in its entirety, without going + // through the write buffer. + bytesWritten = uio_write(stream->handle, buf, bytesToWrite); + if (bytesWritten != bytesToWrite) { + stream->status = uio_Stream_STATUS_ERROR; + if (bytesWritten == -1) + return 0; + } + + if (bytesWritten == bytesToWrite) + return nmemb; + return (size_t) bytesWritten / size; +} + +// NB: stdio fflush() accepts NULL to flush all streams. uio_flush() does +// not. +int +uio_fflush(uio_Stream *stream) { + assert(stream != NULL); + + if (stream->operation == uio_StreamOperation_write) { + if (uio_Stream_flushWriteBuffer(stream) == -1) { + // errno is set + return (int) EOF; + } + stream->operation = uio_StreamOperation_none; + } + + return 0; +} + +int +uio_feof(uio_Stream *stream) { + return stream->status == uio_Stream_STATUS_EOF; +} + +int +uio_ferror(uio_Stream *stream) { + return stream->status == uio_Stream_STATUS_ERROR; +} + +void +uio_clearerr(uio_Stream *stream) { + stream->status = uio_Stream_STATUS_OK; +} + +// Counterpart of fileno() +uio_Handle * +uio_streamHandle(uio_Stream *stream) { + return stream->handle; +} + +#ifndef NDEBUG +static void +uio_assertReadSanity(uio_Stream *stream) { + assert((stream->openFlags & O_ACCMODE) != O_WRONLY); + + if (stream->operation == uio_StreamOperation_write) { + // "[...] output shall not be directly followed by input without an + // intervening call to the fflush function or to a file positioning + // function (fseek, fsetpos, or rewind), and input shall not be + // directly followed by output without an intervening call to a file + // positioning function, unless the input operation encounters + // end-of-file." (POSIX, C) + fprintf(stderr, "Error: Reading on a file directly after writing, " + "without an intervening call to fflush() or a file " + "positioning function.\n"); + abort(); + } +} +#endif + +#ifndef NDEBUG +static void +uio_assertWriteSanity(uio_Stream *stream) { + assert((stream->openFlags & O_ACCMODE) != O_RDONLY); + + if (stream->operation == uio_StreamOperation_read) { + // "[...] output shall not be directly followed by input without an + // intervening call to the fflush function or to a file positioning + // function (fseek, fsetpos, or rewind), and input shall not be + // directly followed by output without an intervening call to a file + // positioning function, unless the input operation encounters + // end-of-file." (POSIX, C) + fprintf(stderr, "Error: Writing on a file directly after reading, " + "without an intervening call to a file positioning " + "function.\n"); + abort(); + } + assert(stream->dataStart == stream->buf); +} +#endif + +static int +uio_Stream_flushWriteBuffer(uio_Stream *stream) { + ssize_t bytesWritten; + + assert(stream->operation == uio_StreamOperation_write); + + bytesWritten = uio_write(stream->handle, stream->dataStart, + stream->dataEnd - stream->dataStart); + if (bytesWritten != stream->dataEnd - stream->dataStart) { + stream->status = uio_Stream_STATUS_ERROR; + return -1; + } + assert(stream->dataStart == stream->buf); + stream->dataEnd = stream->buf; + + return 0; +} + +static void +uio_Stream_discardReadBuffer(uio_Stream *stream) { + assert(stream->operation == uio_StreamOperation_read); + stream->dataStart = stream->buf; + stream->dataEnd = stream->buf; + // TODO: when implementing pushback: throw away pushback buffer. +} + +static int +uio_Stream_fillReadBuffer(uio_Stream *stream) { + ssize_t numRead; + + assert(stream->operation == uio_StreamOperation_read); + + numRead = uio_read(stream->handle, stream->buf, + uio_Stream_BLOCK_SIZE); + if (numRead == -1) + return -1; + stream->dataStart = stream->buf; + stream->dataEnd = stream->buf + numRead; + return 0; +} + +static inline uio_Stream * +uio_Stream_new(uio_Handle *handle, int openFlags) { + uio_Stream *result; + + result = uio_Stream_alloc(); + result->handle = handle; + result->openFlags = openFlags; + result->status = uio_Stream_STATUS_OK; + result->operation = uio_StreamOperation_none; + result->buf = uio_malloc(uio_Stream_BLOCK_SIZE); + result->dataStart = result->buf; + result->dataEnd = result->buf; + result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE; + return result; +} + +static inline uio_Stream * +uio_Stream_alloc(void) { + uio_Stream *result = uio_malloc(sizeof (uio_Stream)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_Stream, (void *) result); +#endif + return result; +} + +static inline void +uio_Stream_delete(uio_Stream *stream) { + uio_free(stream->buf); + uio_Stream_free(stream); +} + +static inline void +uio_Stream_free(uio_Stream *stream) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_Stream, (void *) stream); +#endif + uio_free(stream); +} + diff --git a/src/libs/uio/uiostream.h b/src/libs/uio/uiostream.h new file mode 100644 index 0000000..f65487e --- /dev/null +++ b/src/libs/uio/uiostream.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_UIOSTREAM_H_ +#define LIBS_UIO_UIOSTREAM_H_ + + +typedef struct uio_Stream uio_Stream; + +#include "io.h" + +#include <stdarg.h> + + +uio_Stream *uio_fopen(uio_DirHandle *dir, const char *path, const char *mode); +int uio_fclose(uio_Stream *stream); +size_t uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream); +char *uio_fgets(char *buf, int size, uio_Stream *stream); +int uio_fgetc(uio_Stream *stream); +#define uio_getc uio_fgetc +int uio_ungetc(int c, uio_Stream *stream); +int uio_vfprintf(uio_Stream *stream, const char *format, va_list args); +int uio_fprintf(uio_Stream *stream, const char *format, ...); +int uio_fputc(int c, uio_Stream *stream); +#define uio_putc uio_fputc +int uio_fputs(const char *s, uio_Stream *stream); +int uio_fseek(uio_Stream *stream, long offset, int whence); +long uio_ftell(uio_Stream *stream); +size_t uio_fwrite(const void *buf, size_t size, size_t nmemb, + uio_Stream *stream); +int uio_fflush(uio_Stream *stream); +int uio_feof(uio_Stream *stream); +int uio_ferror(uio_Stream *stream); +void uio_clearerr(uio_Stream *stream); +uio_Handle *uio_streamHandle(uio_Stream *stream); + + +/* *** Internal definitions follow *** */ +#ifdef uio_INTERNAL + +#include <sys/types.h> +#include <fcntl.h> +#include "iointrn.h" + +typedef enum { + uio_StreamOperation_none, + uio_StreamOperation_read, + uio_StreamOperation_write +} uio_StreamOperation; + +struct uio_Stream { + char *buf; + // Start of the buffer. + char *dataStart; + // Start of the part of the buffer that is in use. + char *dataEnd; + // Start of the unused part of the buffer. + char *bufEnd; + // End of the buffer. + // INV: buf <= dataStart <= dataEnd <= bufEnd + // INV: if 'operation == uio_StreamOperation_write' then buf == dataStart + + uio_Handle *handle; + int status; +#define uio_Stream_STATUS_OK 0 +#define uio_Stream_STATUS_EOF 1 +#define uio_Stream_STATUS_ERROR 2 + uio_StreamOperation operation; + // What was the last action (reading or writing). This + // determines whether the buffer is a read or write buffer. + int openFlags; + // Flags used for opening the file. +}; + + +#endif /* uio_INTERNAL */ + +#endif /* LIBS_UIO_UIOSTREAM_H_ */ + + diff --git a/src/libs/uio/uioutils.c b/src/libs/uio/uioutils.c new file mode 100644 index 0000000..fbe3cd4 --- /dev/null +++ b/src/libs/uio/uioutils.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <sys/types.h> +#include <string.h> + +#include "uioutils.h" +#include "mem.h" +#include "paths.h" +#include "uioport.h" + +/** + * Concatenate two strings into a newly allocated buffer. + * + * @param[in] first The first (left) string, '\0' terminated. + * @param[in] second The second (right) string, '\0' terminated. + * + * @returns A newly allocated string consisting of the concatenation of + * 'first' and 'second', to be freed using uio_free(). + */ +char * +strcata(const char *first, const char *second) { + char *result, *resPtr; + size_t firstLen, secondLen; + + firstLen = strlen(first); + secondLen = strlen(second); + result = uio_malloc(firstLen + secondLen + 1); + resPtr = result; + + memcpy(resPtr, first, firstLen); + resPtr += firstLen; + + memcpy(resPtr, second, secondLen); + resPtr += secondLen; + + *resPtr = '\0'; + return result; +} + +// returns a copy of a generic array 'array' with 'element' inserted in +// position 'insertPos' +void * +insertArray(const void *array, size_t oldNumElements, int insertPos, + const void *element, size_t elementSize) { + void *newArray, *newArrayPtr; + const void *arrayPtr; + size_t preInsertSize; + + newArray = uio_malloc((oldNumElements + 1) * elementSize); + preInsertSize = insertPos * elementSize; + memcpy(newArray, array, preInsertSize); + newArrayPtr = (char *) newArray + preInsertSize; + arrayPtr = (const char *) array + preInsertSize; + memcpy(newArrayPtr, element, elementSize); + newArrayPtr = (char *) newArrayPtr + elementSize; + memcpy(newArrayPtr, arrayPtr, + (oldNumElements - insertPos) * elementSize); + return newArray; +} + +// returns a copy of a pointer array 'array' with 'element' inserted in +// position 'insertPos' +void ** +insertArrayPointer(const void **array, size_t oldNumElements, int insertPos, + const void *element) { + void **newArray, **newArrayPtr; + const void **arrayPtr; + size_t preInsertSize; + + newArray = uio_malloc((oldNumElements + 1) * sizeof (void *)); + preInsertSize = insertPos * sizeof (void *); + memcpy(newArray, array, preInsertSize); + newArrayPtr = newArray + insertPos; + arrayPtr = array + insertPos; + *newArrayPtr = unconst(element); + newArrayPtr++; + memcpy(newArrayPtr, arrayPtr, + (oldNumElements - insertPos) * sizeof (void *)); + return newArray; +} + +// returns a copy of a generic array 'array' with 'numExclude' elements, +// starting from startpos, removed. +void * +excludeArray(const void *array, size_t oldNumElements, int startPos, + int numExclude, size_t elementSize) { + void *newArray, *newArrayPtr; + const void *arrayPtr; + size_t preExcludeSize; + + newArray = uio_malloc((oldNumElements - numExclude) * elementSize); + preExcludeSize = startPos * elementSize; + memcpy(newArray, array, preExcludeSize); + newArrayPtr = (char *) newArray + preExcludeSize; + arrayPtr = (const char *) array + + (startPos + numExclude) * sizeof (elementSize); + memcpy(newArrayPtr, arrayPtr, + (oldNumElements - startPos - numExclude) * elementSize); + return newArray; +} + +// returns a copy of a pointer array 'array' with 'numExclude' elements, +// starting from startpos, removed. +void ** +excludeArrayPointer(const void **array, size_t oldNumElements, int startPos, + int numExclude) { + void **newArray; + + newArray = uio_malloc((oldNumElements - numExclude) * sizeof (void *)); + memcpy(newArray, array, startPos * sizeof (void *)); + memcpy(&newArray[startPos], &array[startPos + numExclude], + (oldNumElements - startPos - numExclude) * sizeof (void *)); + return newArray; +} + +// If the given DOS date/time is invalid, the result is unspecified, +// but the function won't crash. +time_t +dosToUnixTime(uio_uint16 date, uio_uint16 tm) { + // DOS date has the following format: + // bits 0-4 specify the number of the day in the month (1-31). + // bits 5-8 specify the number of the month in the year (1-12). + // bits 9-15 specify the year number since 1980 (0-127) + // DOS time has the fillowing format: + // bits 0-4 specify the number of seconds/2 in the minute (0-29) + // (only accurate on 2 seconds) + // bits 5-10 specify the number of minutes in the hour (0-59) + // bits 11-15 specify the number of hours since midnight (0-23) + + int year, month, day; + int hours, minutes, seconds; + long result; + + static const int daysUntilMonth[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + 334, 334, 334, 334 }; + // The last 4 entries are there so that there's no + // invalid memory access if the date is invalid. + + year = date >> 9; + month = ((date >> 5) - 1) & 0x0f; // Number in [0..15] + day = (date - 1) & 0x1f; // Number in [0..31] + hours = tm >> 11; + minutes = (tm >> 5) & 0x3f; + seconds = (tm & 0x1f) * 2; // Even number in [0..62] + + result = year * 365 + daysUntilMonth[month] + day; + // Count the (non-leap) days in all those years + + // Add a leapday for each 4th year + if (year % 4 == 0 && month <= 2) { + // The given date is a leap-year but the leapday hasn't occured yet. + result += year / 4; + } else { + result += 1 + year / 4; + } + // result now is the number of days between 1980-01-01 and the given day. + + // Add the days between 1970-01-01 and 1980-01-01 + // (2 leapdays in this period) + result += 365 * 10 + 2; + + result = (result * 24) + hours; // days to hours + result = (result * 60) + minutes; // hours to minutes + result = (result * 60) + seconds; // minutes to seconds + + return (time_t) result; +} + +char * +dosToUnixPath(const char *path) { + const char *srcPtr; + char *result, *dstPtr; + size_t skip; + + result = uio_malloc(strlen(path) + 1); + srcPtr = path; + dstPtr = result; + + // A UNC path will look like this: "\\server\share/..."; the first two + // characters will be backslashes, and the separator between the server + // and the share too. The rest will be slashes. + // The goal is that at every forward slash, the path should be + // stat()'able. + skip = uio_skipUNCServerShare(srcPtr); + if (skip != 0) { + char *slash; + memcpy(dstPtr, srcPtr, skip); + + slash = memchr(srcPtr + 2, '/', skip - 2); + if (slash != NULL) + *slash = '\\'; + + srcPtr += skip; + dstPtr += skip; + } + + while (*srcPtr != '\0') { + if (*srcPtr == '\\') { + *dstPtr = '/'; + } else + *dstPtr = *srcPtr; + srcPtr++; + dstPtr++; + } + *dstPtr = '\0'; + return result; +} + + diff --git a/src/libs/uio/uioutils.h b/src/libs/uio/uioutils.h new file mode 100644 index 0000000..6e04843 --- /dev/null +++ b/src/libs/uio/uioutils.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_UIOUTILS_H_ +#define LIBS_UIO_UIOUTILS_H_ + +#include <time.h> + +#include "types.h" +#include "uioport.h" + +char *strcata(const char *first, const char *second); +void *insertArray(const void *array, size_t oldNumElements, int insertPos, + const void *element, size_t elementSize); +void **insertArrayPointer(const void **array, size_t oldNumElements, + int insertPos, const void *element); +void *excludeArray(const void *array, size_t oldNumElements, int startPos, + int numExclude, size_t elementSize); +void **excludeArrayPointer(const void **array, size_t oldNumElements, + int startPos, int numExclude); +time_t dosToUnixTime(uio_uint16 date, uio_uint16 tm); +char *dosToUnixPath(const char *path); + +/* Sometimes you just have to remove a 'const'. + * (for instance, when implementing a function like strchr) + */ +static inline void * +unconst(const void *arg) { + union { + void *c; + const void *cc; + } u; + u.cc = arg; + return u.c; +} + +// byte1 is the lowest byte, byte4 the highest +static inline uio_uint32 +makeUInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) { + return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24); +} + +static inline uio_uint16 +makeUInt16(uio_uint8 byte1, uio_uint8 byte2) { + return byte1 | (byte2 << 8); +} + +static inline uio_sint32 +makeSInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) { + return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24); +} + +static inline uio_sint16 +makeSInt16(uio_uint8 byte1, uio_uint8 byte2) { + return byte1 | (byte2 << 8); +} + +static inline uio_bool +isBitSet(uio_uint32 bitField, int bit) { + return ((bitField >> bit) & 1) == 1; +} + +static inline int +mins(int i1, int i2) { + return i1 <= i2 ? i1 : i2; +} + +static inline unsigned int +minu(unsigned int i1, unsigned int i2) { + return i1 <= i2 ? i1 : i2; +} + + +#endif /* LIBS_UIO_UIOUTILS_H_ */ + diff --git a/src/libs/uio/utils.c b/src/libs/uio/utils.c new file mode 100644 index 0000000..1f705bb --- /dev/null +++ b/src/libs/uio/utils.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 <errno.h> +#include <time.h> +#include <stdio.h> +#ifdef _MSC_VER +# include <stdarg.h> +#endif /* _MSC_VER */ + +#include "iointrn.h" +#include "ioaux.h" +#include "utils.h" + +static int uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf); + +struct uio_StdioAccessHandle { + uio_DirHandle *tempRoot; + char *tempDirName; + uio_DirHandle *tempDir; + char *fileName; + char *stdioPath; +}; + +static inline uio_StdioAccessHandle *uio_StdioAccessHandle_new( + uio_DirHandle *tempRoot, char *tempDirName, + uio_DirHandle *tempDir, char *fileName, + char *stdioPath); +static inline void uio_StdioAccessHandle_delete( + uio_StdioAccessHandle *handle); +static inline uio_StdioAccessHandle *uio_StdioAccessHandle_alloc(void); +static inline void uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle); + +/* + * Copy a file with path srcName to a file with name newName. + * If the destination already exists, the operation fails. + * Links are followed. + * Special files (fifos, char devices, block devices, etc) will be + * read as long as there is data available and the destination will be + * a regular file with that data. + * The new file will have the same permissions as the old. + * If an error occurs during copying, an attempt will be made to + * remove the copy. + */ +int +uio_copyFile(uio_DirHandle *srcDir, const char *srcName, + uio_DirHandle *dstDir, const char *newName) { + uio_Handle *src, *dst; + struct stat sb; +#define BUFSIZE 65536 + uio_uint8 *buf, *bufPtr; + ssize_t numInBuf, numWritten; + + src = uio_open(srcDir, srcName, O_RDONLY +#ifdef WIN32 + | O_BINARY +#endif + , 0); + if (src == NULL) + return -1; + + if (uio_fstat(src, &sb) == -1) + return uio_copyError(src, NULL, NULL, NULL, NULL); + + dst = uio_open(dstDir, newName, O_WRONLY | O_CREAT | O_EXCL +#ifdef WIN32 + | O_BINARY +#endif + , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + if (dst == NULL) + return uio_copyError(src, NULL, NULL, NULL, NULL); + + buf = uio_malloc(BUFSIZE); + // This was originally a statically allocated buffer, + // but as this function might be run from a thread with + // a small Stack, this is better. + while (1) { + numInBuf = uio_read(src, buf, BUFSIZE); + if (numInBuf == -1) { + if (errno == EINTR) + continue; + return uio_copyError(src, dst, dstDir, newName, buf); + } + if (numInBuf == 0) + break; + + bufPtr = buf; + do + { + numWritten = uio_write(dst, bufPtr, numInBuf); + if (numWritten == -1) { + if (errno == EINTR) + continue; + return uio_copyError(src, dst, dstDir, newName, buf); + } + numInBuf -= numWritten; + bufPtr += numWritten; + } while (numInBuf > 0); + } + + uio_free(buf); + uio_close(src); + uio_close(dst); + errno = 0; + return 0; +} + +/* + * Closes srcHandle if it's not -1. + * Closes dstHandle if it's not -1. + * Removes unlinkpath from the unlinkHandle dir if it's not NULL. + * Frees 'buf' if not NULL. + * Always returns -1. + * errno is what was before the call. + */ +static int +uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf) { + int savedErrno; + + savedErrno = errno; + +#ifdef DEBUG + fprintf(stderr, "Error while copying: %s\n", strerror(errno)); +#endif + + if (srcHandle != NULL) + uio_close(srcHandle); + + if (dstHandle != NULL) + uio_close(dstHandle); + + if (unlinkPath != NULL) + uio_unlink(unlinkHandle, unlinkPath); + + if (buf != NULL) + uio_free(buf); + + errno = savedErrno; + return -1; +} + +#define NUM_TEMP_RETRIES 16 + // Retry this many times to create a temporary dir, before giving + // up. If undefined, keep trying indefinately. + +uio_StdioAccessHandle * +uio_getStdioAccess(uio_DirHandle *dir, const char *path, int flags, + uio_DirHandle *tempDir) { + int res; + uio_MountHandle *mountHandle; + const char *name; + char *newPath; + char *tempDirName; + uio_DirHandle *newDir; + uio_FileSystemID fsID; + + res = uio_getFileLocation(dir, path, flags, &mountHandle, &newPath); + if (res == -1) { + // errno is set + return NULL; + } + + fsID = uio_getMountFileSystemType(mountHandle); + if (fsID == uio_FSTYPE_STDIO) { + // Current location is usable. + return uio_StdioAccessHandle_new(NULL, NULL, NULL, NULL, newPath); + } + uio_free(newPath); + + { + uio_uint32 dirNum; + int i; + + // Current location is not usable. Create a directory with a + // generated name, as a temporary location to store a copy of + // the file. + dirNum = (uio_uint32) time(NULL); + tempDirName = uio_malloc(sizeof "01234567"); + for (i = 0; ; i++) { +#ifdef NUM_TEMP_RETRIES + if (i >= NUM_TEMP_RETRIES) { + // Using ENOSPC to report that we couldn't create a + // temporary dir, getting EEXIST. + uio_free(tempDirName); + errno = ENOSPC; + return NULL; + } +#endif + + sprintf(tempDirName, "%08lx", (unsigned long) dirNum + i); + + res = uio_mkdir(tempDir, tempDirName, 0700); + if (res == -1) { + int savedErrno; + if (errno == EEXIST) + continue; + savedErrno = errno; +#ifdef DEBUG + fprintf(stderr, "Error: Could not create temporary dir: %s\n", + strerror(errno)); +#endif + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + break; + } + + newDir = uio_openDirRelative(tempDir, tempDirName, 0); + if (newDir == NULL) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not open temporary dir: %s\n", + strerror(errno)); +#endif + res = uio_rmdir(tempDir, tempDirName); +#ifdef DEBUG + if (res == -1) + fprintf(stderr, "Warning: Could not remove temporary dir: " + "%s.\n", strerror(errno)); +#endif + uio_free(tempDirName); + errno = EIO; + return NULL; + } + + // Get the last component of path. This should be the file to + // access. + name = strrchr(path, '/'); + if (name == NULL) + name = path; + + // Copy the file + res = uio_copyFile(dir, path, newDir, name); + if (res == -1) { + int savedErrno = errno; +#ifdef DEBUG + fprintf(stderr, "Error: Could not copy file to temporary dir: " + "%s\n", strerror(errno)); +#endif + uio_closeDir(newDir); + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + } + + res = uio_getFileLocation(newDir, name, flags, &mountHandle, &newPath); + if (res == -1) { + int savedErrno = errno; + fprintf(stderr, "Error: uio_getStdioAccess: Could not get location " + "of temporary dir: %s.\n", strerror(errno)); + uio_closeDir(newDir); + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + + fsID = uio_getMountFileSystemType(mountHandle); + if (fsID != uio_FSTYPE_STDIO) { + // Temp dir isn't on a stdio fs either. + fprintf(stderr, "Error: uio_getStdioAccess: Temporary file location " + "isn't on a stdio filesystem.\n"); + uio_closeDir(newDir); + uio_free(tempDirName); + uio_free(newPath); +// errno = EXDEV; + errno = EINVAL; + return NULL; + } + + uio_DirHandle_ref(tempDir); + return uio_StdioAccessHandle_new(tempDir, tempDirName, newDir, + uio_strdup(name), newPath); +} + +void +uio_releaseStdioAccess(uio_StdioAccessHandle *handle) { + if (handle->tempDir != NULL) { + if (uio_unlink(handle->tempDir, handle->fileName) == -1) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not remove temporary file: " + "%s\n", strerror(errno)); +#endif + } + + // Need to free this handle in advance. There should be no handles + // to a dir left when removing it. + uio_DirHandle_unref(handle->tempDir); + handle->tempDir = NULL; + + if (uio_rmdir(handle->tempRoot, handle->tempDirName) == -1) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not remove temporary directory: " + "%s\n", strerror(errno)); +#endif + } + } + + uio_StdioAccessHandle_delete(handle); +} + +const char * +uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle) { + return (const char *) handle->stdioPath; +} + +// references to tempRoot and tempDir are not increased. +// no copies of arguments are made. +// By calling this function control of the values is transfered to +// the handle. +static inline uio_StdioAccessHandle * +uio_StdioAccessHandle_new( + uio_DirHandle *tempRoot, char *tempDirName, + uio_DirHandle *tempDir, char *fileName, char *stdioPath) { + uio_StdioAccessHandle *result; + + result = uio_StdioAccessHandle_alloc(); + result->tempRoot = tempRoot; + result->tempDirName = tempDirName; + result->tempDir = tempDir; + result->fileName = fileName; + result->stdioPath = stdioPath; + + return result; +} + +static inline void +uio_StdioAccessHandle_delete(uio_StdioAccessHandle *handle) { + if (handle->tempDir != NULL) + uio_DirHandle_unref(handle->tempDir); + if (handle->fileName != NULL) + uio_free(handle->fileName); + if (handle->tempRoot != NULL) + uio_DirHandle_unref(handle->tempRoot); + if (handle->tempDirName != NULL) + uio_free(handle->tempDirName); + uio_free(handle->stdioPath); + uio_StdioAccessHandle_free(handle); +} + +static inline uio_StdioAccessHandle * +uio_StdioAccessHandle_alloc(void) { + return uio_malloc(sizeof (uio_StdioAccessHandle)); +} + +static inline void +uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle) { + uio_free(handle); +} + +#ifdef _MSC_VER +# include <stdarg.h> +#if 0 /* Unneeded for now */ +// MSVC does not have snprintf(). It does have a _snprintf(), but it does +// not \0-terminate a truncated string as the C standard prescribes. +static inline int +snprintf(char *str, size_t size, const char *format, ...) +{ + int result; + va_list args; + + va_start (args, format); + result = _vsnprintf (str, size, format, args); + if (str != NULL && size != 0) + str[size - 1] = '\0'; + va_end (args); + + return result; +} +#endif + +// MSVC does not have vsnprintf(). It does have a _vsnprintf(), but it does +// not \0-terminate a truncated string as the C standard prescribes. +static inline int +vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + int result = _vsnprintf (str, size, format, args); + if (str != NULL && size != 0) + str[size - 1] = '\0'; + return result; +} +#endif /* _MSC_VER */ + +// The result should be freed using uio_free(). +// NB. POSIX allows errno to be set for vsprintf(), but does not require it: +// "The value of errno may be set to nonzero by a library function call +// whether or not there is an error, provided the use of errno is not +// documented in the description of the function in this International +// Standard." The latter is the case for vsprintf(). +char * +uio_vasprintf(const char *format, va_list args) { + // TODO: If there is a system vasprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + char *buf; + size_t bufSize = 128; + // Start with enough for one screen line, and a power of 2, + // which might give faster result with allocations. + + buf = uio_malloc(bufSize); + if (buf == NULL) { + // errno is set. + return NULL; + } + + for (;;) { + int printResult = vsnprintf(buf, bufSize, format, args); + if (printResult < 0) { + // This means the buffer was not large enough, but vsnprintf() + // does not give us any clue on how large it should be. + // Note that this does not happen with a C'99 compliant + // vsnprintf(), but it will happen on MS Windows, and on + // glibc before version 2.1. + bufSize *= 2; + } else if ((unsigned int) printResult >= bufSize) { + // The buffer was too small, but printResult contains the size + // that the buffer needs to be (excluding the '\0' character). + bufSize = printResult + 1; + } else { + // Success. + if ((unsigned int) printResult + 1 != bufSize) { + // Shorten the resulting buffer to the size that was + // actually needed. + char *newBuf = uio_realloc(buf, printResult + 1); + if (newBuf == NULL) { + // We could have returned the (overly large) original + // buffer, but the unused memory might not be + // acceptable, and the program would be likely to run + // into problems sooner or later anyhow. + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + return newBuf; + } + + return buf; + } + + { + char *newBuf = uio_realloc(buf, bufSize); + if (newBuf == NULL) + { + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + buf = newBuf; + } + } +} + +// As uio_vasprintf(), but with an argument list. +char * +uio_asprintf(const char *format, ...) { + // TODO: If there is a system asprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + va_list args; + char *result; + int savedErrno; + + va_start(args, format); + result = uio_vasprintf(format, args); + savedErrno = errno; + va_end(args); + + errno = savedErrno; + return result; +} + + diff --git a/src/libs/uio/utils.h b/src/libs/uio/utils.h new file mode 100644 index 0000000..313bf67 --- /dev/null +++ b/src/libs/uio/utils.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 LIBS_UIO_UTILS_H_ +#define LIBS_UIO_UTILS_H_ + +#include <stdarg.h> + +#ifdef uio_INTERNAL +typedef struct uio_StdioAccessHandle uio_StdioAccessHandle; +#else +typedef void uio_StdioAccessHandle; +#endif + +int uio_copyFile(uio_DirHandle *srcDir, const char *srcName, + uio_DirHandle *dstDir, const char *newName); +uio_StdioAccessHandle *uio_getStdioAccess(uio_DirHandle *dir, + const char *path, int flags, uio_DirHandle *tempDir); +const char *uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle); +void uio_releaseStdioAccess(uio_StdioAccessHandle *handle); + +char *uio_vasprintf(const char *format, va_list args); +char *uio_asprintf(const char *format, ...); + +#endif /* LIBS_UIO_UTILS_H_ */ + diff --git a/src/libs/uio/zip/Makeinfo b/src/libs/uio/zip/Makeinfo new file mode 100644 index 0000000..64ae8d5 --- /dev/null +++ b/src/libs/uio/zip/Makeinfo @@ -0,0 +1,2 @@ +uqm_CFILES="zip.c" +uqm_HFILES="zip.h" diff --git a/src/libs/uio/zip/zip.c b/src/libs/uio/zip/zip.c new file mode 100644 index 0000000..6da462b --- /dev/null +++ b/src/libs/uio/zip/zip.c @@ -0,0 +1,1680 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +/* + * This file makes use of zlib (http://www.gzip.org/zlib/) + * + * References: + * The .zip format description from PKWare: + * http://www.pkware.com/products/enterprise/white_papers/appnote.html + * The .zip format description from InfoZip: + * ftp://ftp.info-zip.org/pub/infozip/doc/appnote-011203-iz.zip + */ + +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> + +#include "zip.h" +#include "../physical.h" +#include "../uioport.h" +#include "../paths.h" +#include "../uioutils.h" +#ifdef uio_MEM_DEBUG +# include "../memdebug.h" +#endif + + +#define DIR_STRUCTURE_READ_BUFSIZE 0x10000 + +static int zip_badFile(zip_GPFileData *gPFileData, char *fileName); +static int zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle); +#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS +static int zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle); +static int zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir, + uio_FileBlock *fileBlock, off_t *pos); +#endif +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +static off_t zip_findEndOfCentralDirectoryRecord(uio_Handle *handle, + uio_FileBlock *fileBlock); +static int zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle); +static int zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir, + uio_FileBlock *fileBlock, off_t *pos); +static int zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData, + uio_FileBlock *fileBlock, int pos); +int zip_updateFileDataFromLocalHeader(uio_Handle *handle, + zip_GPFileData *gPFileData); +#endif +static int zip_fillDirStructureProcessExtraFields( + uio_FileBlock *fileBlock, off_t extraFieldLength, + zip_GPFileData *gPFileData, const char *path, off_t pos, + uio_bool central); +static inline int zip_foundFile(uio_GPDir *gPDir, const char *path, + zip_GPFileData *gPFileData); +static inline int zip_foundDir(uio_GPDir *gPDir, const char *dirName, + zip_GPDirData *gPDirData); +static int zip_initZipStream(z_stream *zipStream); +static int zip_unInitZipStream(z_stream *zipStream); +static int zip_reInitZipStream(z_stream *zipStream); + +static voidpf zip_alloc(voidpf opaque, uInt items, uInt size); +static void zip_free(voidpf opaque, voidpf address); + +static inline zip_GPFileData * zip_GPFileData_new(void); +static inline void zip_GPFileData_delete(zip_GPFileData *gPFileData); +static inline zip_GPFileData *zip_GPFileData_alloc(void); +static inline void zip_GPFileData_free(zip_GPFileData *gPFileData); +static inline void zip_GPDirData_delete(zip_GPDirData *gPDirData); +static inline void zip_GPDirData_free(zip_GPDirData *gPDirData); + +static ssize_t zip_readStored(uio_Handle *handle, void *buf, size_t count); +static ssize_t zip_readDeflated(uio_Handle *handle, void *buf, size_t count); +static off_t zip_seekStored(uio_Handle *handle, off_t offset); +static off_t zip_seekDeflated(uio_Handle *handle, off_t offset); + +uio_FileSystemHandler zip_fileSystemHandler = { + /* .init = */ NULL, + /* .unInit = */ NULL, + /* .cleanup = */ NULL, + + /* .mount = */ zip_mount, + /* .umount = */ uio_GPRoot_umount, + + /* .access = */ zip_access, + /* .close = */ zip_close, + /* .fstat = */ zip_fstat, + /* .stat = */ zip_stat, + /* .mkdir = */ NULL, + /* .open = */ zip_open, + /* .read = */ zip_read, + /* .rename = */ NULL, + /* .rmdir = */ NULL, + /* .seek = */ zip_seek, + /* .write = */ NULL, + /* .unlink = */ NULL, + + /* .openEntries = */ uio_GPDir_openEntries, + /* .readEntries = */ uio_GPDir_readEntries, + /* .closeEntries = */ uio_GPDir_closeEntries, + + /* .getPDirEntryHandle = */ uio_GPDir_getPDirEntryHandle, + /* .deletePRootExtra = */ uio_GPRoot_delete, + /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete, + /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete, +}; + +uio_GPRoot_Operations zip_GPRootOperations = { + /* .fillGPDir = */ NULL, + /* .deleteGPRootExtra = */ NULL, + /* .deleteGPDirExtra = */ zip_GPDirData_delete, + /* .deleteGPFileExtra = */ zip_GPFileData_delete, +}; + + +#define NUM_COMPRESSION_METHODS 11 +/* + * [0] = stored uncompressed + * [1] = Shrunk + * [2] = Reduced with compression factor 1 + * [3] = Reduced with compression factor 2 + * [4] = Reduced with compression factor 3 + * [5] = Reduced with compression factor 4 + * [6] = Imploded + * [7] = Reserved for Tokenizing + * [8] = Deflated + * [9] = Deflate64 + * [10] = "PKWARE Data Compression Library Imploding" + */ +static const uio_bool + zip_compressionMethodSupported[NUM_COMPRESSION_METHODS] = { + true, false, false, false, false, false, false, false, + true, false, false }; + +typedef ssize_t (*zip_readFunctionType)(uio_Handle *handle, + void *buf, size_t count); +zip_readFunctionType zip_readMethods[NUM_COMPRESSION_METHODS] = { + zip_readStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + zip_readDeflated, NULL, NULL +}; +typedef off_t (*zip_seekFunctionType)(uio_Handle *handle, off_t offset); +zip_seekFunctionType zip_seekMethods[NUM_COMPRESSION_METHODS] = { + zip_seekStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + zip_seekDeflated, NULL, NULL +}; + + +typedef enum { + zip_OSType_FAT, + zip_OSType_Amiga, + zip_OSType_OpenVMS, + zip_OSType_UNIX, + zip_OSType_VMCMS, + zip_OSType_AtariST, + zip_OSType_HPFS, + zip_OSType_HFS, + zip_OSType_ZSystem, + zip_OSType_CPM, + zip_OSType_TOPS20, + zip_OSType_NTFS, + zip_OSType_QDOS, + zip_OSType_Acorn, + zip_OSType_VFAT, + zip_OSType_MVS, + zip_OSType_BeOS, + zip_OSType_Tandem, + + zip_numOSTypes +} zip_OSType; + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +static mode_t zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes); +#endif + +#define zip_INPUT_BUFFER_SIZE 0x10000 + // TODO: make this configurable a la sysctl? +#define zip_SEEK_BUFFER_SIZE zip_INPUT_BUFFER_SIZE + + +void +zip_close(uio_Handle *handle) { + zip_Handle *zip_handle; + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "zip_close - handle=%p\n", (void *) handle); +#endif + zip_handle = handle->native; + uio_GPFile_unref(zip_handle->file); + zip_unInitZipStream(&zip_handle->zipStream); + uio_closeFileBlock(zip_handle->fileBlock); + uio_free(zip_handle); +} + +static void +zip_fillStat(struct stat *statBuf, const zip_GPFileData *gPFileData) { + memset(statBuf, '\0', sizeof (struct stat)); + statBuf->st_size = gPFileData->uncompressedSize; + statBuf->st_uid = gPFileData->uid; + statBuf->st_gid = gPFileData->gid; + statBuf->st_mode = gPFileData->mode; + statBuf->st_atime = gPFileData->atime; + statBuf->st_mtime = gPFileData->mtime; + statBuf->st_ctime = gPFileData->ctime; +} + +int +zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode) { + errno = ENOSYS; // Not implemented. + (void) pDirHandle; + (void) name; + (void) mode; + return -1; + +#if 0 + uio_GPDirEntry *entry; + + if (name[0] == '.' && name[1] == '\0') { + entry = (uio_GPDirEntry *) pDirHandle->extra; + } else { + entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (entry == NULL) { + errno = ENOENT; + return -1; + } + } + + if (mode & R_OK) + { + // Read permission is always granted. Nothing to check here. + } + + if (mode & W_OK) { + errno = EACCES; + return -1; + } + + if (mode & X_OK) { + if (uio_GPDirEntry_isDir(entry)) { + // Search permission on directories is always granted. + } else { + // WORK +#error + } + } + + if (mode & F_OK) { + // WORK +#error + } + + return 0; +#endif +} + +int +zip_fstat(uio_Handle *handle, struct stat *statBuf) { +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + if (handle->native->file->extra->fileOffset == -1) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(handle->root->handle, + handle->native->file->extra) == -1) { + // errno is set + return -1; + } + } +#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */ + zip_fillStat(statBuf, handle->native->file->extra); + return 0; +} + +int +zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) { + uio_GPDirEntry *entry; + + if (name[0] == '.' && name[1] == '\0') { + entry = (uio_GPDirEntry *) pDirHandle->extra; + } else { + entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (entry == NULL) { + errno = ENOENT; + return -1; + } + } + + if (uio_GPDirEntry_isDir(entry) && entry->extra == NULL) { + // No information about this directory was stored. + // We'll have to make something up. + memset(statBuf, '\0', sizeof (struct stat)); + statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IWGRP | S_IXOTH | + S_IROTH | S_IWOTH | S_IXOTH; + statBuf->st_uid = 0; + statBuf->st_gid = 0; + return 0; + } + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +#ifndef zip_INCOMPLETE_STAT + if (((zip_GPFileData *) entry->extra)->fileOffset == -1) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle, + (zip_GPFileData *) entry->extra) == -1) { + // errno is set + return -1; + } + } +#endif /* !defined(zip_INCOMPLETE_STAT) */ +#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */ + + zip_fillStat(statBuf, (zip_GPFileData *) entry->extra); + return 0; +} + +/* + * Function name: zip_open + * Description: open a file in zip file + * Arguments: pDirHandle - handle to the dir where to open the file + * name - the name of the file to open + * flags - flags, as to stdio open() + * mode - mode, as to stdio open() + * Returns: handle, as from stdio open() + * If failed, errno is set and handle is -1. + */ +uio_Handle * +zip_open(uio_PDirHandle *pDirHandle, const char *name, int flags, + mode_t mode) { + zip_Handle *handle; + uio_GPFile *gPFile; + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "zip_open - pDirHandle=%p name=%s flags=%d mode=0%o\n", + (void *) pDirHandle, name, flags, mode); +#endif + + if ((flags & O_ACCMODE) != O_RDONLY) { + errno = EACCES; + return NULL; + } + + gPFile = (uio_GPFile *) uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (gPFile == NULL) { + errno = ENOENT; + return NULL; + } + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + if (gPFile->extra->fileOffset == -1) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle, + gPFile->extra) == -1) { + // errno is set + return NULL; + } + } +#endif + + handle = uio_malloc(sizeof (zip_Handle)); + uio_GPFile_ref(gPFile); + handle->file = gPFile; + handle->fileBlock = uio_openFileBlock2(pDirHandle->pRoot->handle, + gPFile->extra->fileOffset, gPFile->extra->compressedSize); + if (handle->fileBlock == NULL) { + // errno is set + return NULL; + } + + if (zip_initZipStream(&handle->zipStream) == -1) { + uio_GPFile_unref(gPFile); + uio_closeFileBlock(handle->fileBlock); + return NULL; + } + handle->compressedOffset = 0; + handle->uncompressedOffset = 0; + + (void) mode; + return uio_Handle_new(pDirHandle->pRoot, handle, flags); +} + +ssize_t +zip_read(uio_Handle *handle, void *buf, size_t count) { + ssize_t result; + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "zip_read - handle=%p buf=%p count=%d: ", (void *) handle, + (void *) buf, count); +#endif + result = zip_readMethods[handle->native->file->extra->compressionMethod] + (handle, buf, count); +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "%d\n", result); +#endif + return result; +} + +static ssize_t +zip_readStored(uio_Handle *handle, void *buf, size_t count) { + int numBytes; + zip_Handle *zipHandle; + + zipHandle = handle->native; + numBytes = uio_copyFileBlock(zipHandle->fileBlock, + zipHandle->uncompressedOffset, buf, count); + if (numBytes == -1) { + // errno is set + return -1; + } + zipHandle->uncompressedOffset += numBytes; + zipHandle->compressedOffset += numBytes; + return numBytes; +} + +static ssize_t +zip_readDeflated(uio_Handle *handle, void *buf, size_t count) { + zip_Handle *zipHandle; + int inflateResult; + + zipHandle = handle->native; + + if (count > ((zip_GPFileData *) (zipHandle->file->extra))-> + uncompressedSize - zipHandle->zipStream.total_out) + count = ((zip_GPFileData *) (zipHandle->file->extra))-> + uncompressedSize - zipHandle->zipStream.total_out; + + zipHandle->zipStream.next_out = (Bytef *) buf; + zipHandle->zipStream.avail_out = count; + while (zipHandle->zipStream.avail_out > 0) { + if (zipHandle->zipStream.avail_in == 0) { + ssize_t numBytes; + numBytes = uio_accessFileBlock(zipHandle->fileBlock, + zipHandle->compressedOffset, zip_INPUT_BUFFER_SIZE, + (char **) &zipHandle->zipStream.next_in); + if (numBytes == -1) { + // errno is set + return -1; + } +#if 0 + if (numBytes == 0) { + if (zipHandle->uncompressedOffset != + zipHandle->file->extra->uncompressedSize) { + // premature eof + errno = EIO; + return -1; + } + break; + } +#endif + zipHandle->zipStream.avail_in = numBytes; + zipHandle->compressedOffset += numBytes; + } + inflateResult = inflate(&zipHandle->zipStream, Z_SYNC_FLUSH); + zipHandle->uncompressedOffset = zipHandle->zipStream.total_out; + if (inflateResult == Z_STREAM_END) { + // Everything is decompressed + break; + } + if (inflateResult != Z_OK) { + switch (inflateResult) { + case Z_VERSION_ERROR: + fprintf(stderr, "Error: Incompatible version problem for " + " decompression.\n"); + break; + case Z_NEED_DICT: + fprintf(stderr, "Error: Decompressing requires " + "preset dictionary.\n"); + break; + case Z_DATA_ERROR: + fprintf(stderr, "Error: Compressed file is corrupted.\n"); + break; + case Z_STREAM_ERROR: + // This means zipHandle->zipStream is bad, which is + // most likely an error in the code using zlib. + fprintf(stderr, "Fatal: internal error using zlib.\n"); + abort(); + break; + case Z_MEM_ERROR: + fprintf(stderr, "Error: Not enough memory available " + "while decompressing.\n"); + break; + case Z_BUF_ERROR: + // No progress possible. Probably caused by premature + // end of input file. + fprintf(stderr, "Error: When decompressing: premature " + "end of input file.\n"); + errno = EIO; + return -1; +#if 0 + // If this happens, either the input buffer is empty + // or the output buffer is full. This should not happen. + fprintf(stderr, "Fatal: internal error using zlib: " + " no progress is possible in decompression.\n"); + abort(); + break; +#endif + default: + fprintf(stderr, "Fatal: unknown error from inflate().\n"); + abort(); + } + if (zipHandle->zipStream.msg != NULL) + fprintf(stderr, "ZLib reports: %s\n", + zipHandle->zipStream.msg); + errno = EIO; + // Using EIO to report an error in the backend. + return -1; + } + } + return count - zipHandle->zipStream.avail_out; +} + +off_t +zip_seek(uio_Handle *handle, off_t offset, int whence) { + zip_Handle *zipHandle; + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "zip_seek - handle=%p offset=%d whence=%s\n", + (void *) handle, (int) offset, + whence == SEEK_SET ? "SEEK_SET" : + whence == SEEK_CUR ? "SEEK_CUR" : + whence == SEEK_END ? "SEEK_END" : "INVALID"); +#endif + zipHandle = handle->native; + + assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); + switch(whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += zipHandle->uncompressedOffset; + break; + case SEEK_END: + offset += zipHandle->file->extra->uncompressedSize; + break; + } + if (offset < 0) { + offset = 0; + } else if (offset > zipHandle->file->extra->uncompressedSize) { + offset = zipHandle->file->extra->uncompressedSize; + } + return zip_seekMethods[handle->native->file->extra->compressionMethod] + (handle, offset); +} + +static off_t +zip_seekStored(uio_Handle *handle, off_t offset) { + zip_Handle *zipHandle; + + zipHandle = handle->native; + if (offset > zipHandle->file->extra->uncompressedSize) + offset = zipHandle->file->extra->uncompressedSize; + + zipHandle->compressedOffset = offset; + zipHandle->uncompressedOffset = offset; + return offset; +} + +static off_t +zip_seekDeflated(uio_Handle *handle, off_t offset) { + zip_Handle *zipHandle; + + zipHandle = handle->native; + + if (offset < zipHandle->uncompressedOffset) { + // The new offset is earlier than the current offset. We need to + // seek from the beginning. + if (zip_reInitZipStream(&zipHandle->zipStream) == -1) { + // Need to abort. Handle would get in an inconsistent state. + // Should not fail anyhow. + fprintf(stderr, "Fatal: Could not reinitialise zip stream: " + "%s.\n", strerror(errno)); + abort(); + } + zipHandle->compressedOffset = 0; + zipHandle->uncompressedOffset = 0; + } + + if (offset == zipHandle->uncompressedOffset) + return offset; + + // Seek from the current position. + { + char *buffer; + ssize_t numRead; + size_t toRead; + + buffer = uio_malloc(zip_SEEK_BUFFER_SIZE); + toRead = offset - zipHandle->uncompressedOffset; + while (toRead > 0) { + numRead = zip_read(handle, buffer, + toRead < zip_SEEK_BUFFER_SIZE ? + toRead : zip_SEEK_BUFFER_SIZE); + if (numRead == -1) { + fprintf(stderr, "Warning: Could not read zipped file: %s\n", + strerror(errno)); + break; + // The current location is returned. + } + toRead -= numRead; + } + uio_free(buffer); + } + return zipHandle->uncompressedOffset; +} + +uio_PRoot * +zip_mount(uio_Handle *handle, int flags) { + uio_PRoot *result; + uio_PDirHandle *rootDirHandle; + + if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) { + errno = EACCES; + return NULL; + } + + uio_Handle_ref(handle); + result = uio_GPRoot_makePRoot( + uio_getFileSystemHandler(uio_FSTYPE_ZIP), flags, + &zip_GPRootOperations, NULL, uio_GPRoot_PERSISTENT, + handle, NULL, uio_GPDir_COMPLETE); + + rootDirHandle = uio_PRoot_getRootDirHandle(result); + if (zip_fillDirStructure(rootDirHandle->extra, handle) == -1) { + int savedErrno = errno; +#ifdef DEBUG + fprintf(stderr, "Error: failed to read the zip directory " + "structure - %s.\n", strerror(errno)); +#endif + uio_GPRoot_umount(result); + errno = savedErrno; + return NULL; + } + + return result; +} + +static int +zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle) { +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + return zip_fillDirStructureCentral(top, handle); +#endif +#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS + return zip_fillDirStructureLocal(top, handle); +#endif +} + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +static int +zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle) { + uio_FileBlock *fileBlock; + off_t pos; + char *buf; + ssize_t numBytes; + uio_uint16 numEntries; + // TODO: use numEntries to initialise the hash table + // to a smart size + off_t eocdr; + off_t startCentralDir; + + fileBlock = uio_openFileBlock(handle); + if (fileBlock == NULL) { + // errno is set + goto err; + } + + // first find the 'End of Central Directory Record' + eocdr = zip_findEndOfCentralDirectoryRecord(handle, fileBlock); + if (eocdr == -1) { + // errno is set + goto err; + } + + numBytes = uio_accessFileBlock(fileBlock, eocdr, 22, &buf); + if (numBytes == -1) { + // errno is set + goto err; + } + if (numBytes != 22) { + errno = EIO; + goto err; + } + + numEntries = makeUInt16(buf[10], buf[11]); + if (numEntries == 0xffff) { + fprintf(stderr, "Error: Zip64 .zip files are not supported.\n"); + errno = ENOSYS; + goto err; + } + + startCentralDir = makeUInt32(buf[16], buf[17], buf[18], buf[19]); + + // Enable read-ahead buffering, for speed. + uio_setFileBlockUsageHint(fileBlock, uio_FB_USAGE_FORWARD, + DIR_STRUCTURE_READ_BUFSIZE); + + pos = startCentralDir; + while (numEntries--) { + if (zip_fillDirStructureCentralProcessEntry(top, fileBlock, &pos) + == -1) { + // errno is set + goto err; + } + } + + uio_closeFileBlock(fileBlock); + return 0; + +err: + { + int savedErrno = errno; + + if (fileBlock != NULL) + uio_closeFileBlock(fileBlock); + errno = savedErrno; + return -1; + } +} + +static int +zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir, + uio_FileBlock *fileBlock, off_t *pos) { + char *buf; + zip_GPFileData *gPFileData; + ssize_t numBytes; + + uio_uint32 signature; + uio_uint16 lastModTime; + uio_uint16 lastModDate; + uio_uint32 crc; + uio_uint16 fileNameLength; + uio_uint16 extraFieldLength; + uio_uint16 fileCommentLength; + char *fileName; + zip_OSType creatorOS; + + off_t nextEntryOffset; + + numBytes = uio_accessFileBlock(fileBlock, *pos, 46, &buf); + if (numBytes != 46) + return zip_badFile(NULL, NULL); + + signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]); + if (signature != 0x02014b50) { + fprintf(stderr, "Error: Premature end of central directory.\n"); + errno = EIO; + return -1; + } + + gPFileData = zip_GPFileData_new(); + creatorOS = (zip_OSType) buf[5]; + gPFileData->compressionFlags = makeUInt16(buf[8], buf[9]); + gPFileData->compressionMethod = makeUInt16(buf[10], buf[11]); + lastModTime = makeUInt16(buf[12], buf[13]); + lastModDate = makeUInt16(buf[14], buf[15]); + gPFileData->atime = (time_t) 0; + gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime); + gPFileData->ctime = (time_t) 0; + crc = makeUInt32(buf[16], buf[17], buf[18], buf[19]); + gPFileData->compressedSize = + makeUInt32(buf[20], buf[21], buf[22], buf[23]); + gPFileData->uncompressedSize = + makeUInt32(buf[24], buf[25], buf[26], buf[27]); + fileNameLength = makeUInt16(buf[28], buf[29]); + extraFieldLength = makeUInt16(buf[30], buf[31]); + fileCommentLength = makeUInt16(buf[32], buf[33]); + gPFileData->uid = 0; + gPFileData->gid = 0; + gPFileData->mode = zip_makeFileMode(creatorOS, + makeUInt32(buf[38], buf[39], buf[40], buf[41])); + + gPFileData->headerOffset = + (off_t) makeSInt32(buf[42], buf[43], buf[44], buf[45]); + gPFileData->fileOffset = (off_t) -1; + + *pos += 46; + nextEntryOffset = *pos + fileNameLength + extraFieldLength + + fileCommentLength; + + numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf); + if (numBytes != fileNameLength) + return zip_badFile(gPFileData, NULL); + fileName = uio_malloc(fileNameLength + 1); + memcpy(fileName, buf, fileNameLength); + fileName[fileNameLength] = '\0'; + *pos += fileNameLength; + + if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS || + !zip_compressionMethodSupported[gPFileData->compressionMethod]) { + fprintf(stderr, "Warning: File '%s' is compressed with " + "unsupported method %d - skipped.\n", fileName, + gPFileData->compressionMethod); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + if (gPFileData->compressedSize == (off_t) 0xffffffff || + gPFileData->uncompressedSize == (off_t) 0xffffffff || + gPFileData->headerOffset < 0) { + fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + if (isBitSet(gPFileData->compressionFlags, 0)) { + fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + switch (zip_fillDirStructureProcessExtraFields(fileBlock, + extraFieldLength, gPFileData, fileName, *pos, true)) { + case 0: // file is ok + break; + case 1: // file is not acceptable - skip file + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + case -1: + return zip_badFile(gPFileData, fileName); + } + + *pos += extraFieldLength; + + // If ctime or atime is 0, they will be filled in when the local + // file header is read. + + if (S_ISREG(gPFileData->mode)) { + if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found file '%s'.\n", fileName); +#endif + } else if (S_ISDIR(gPFileData->mode)) { + if (fileName[fileNameLength - 1] == '/') + fileName[fileNameLength - 1] = '\0'; + if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found dir '%s'.\n", fileName); +#endif + } else { + fprintf(stderr, "Warning: '%s' is not a regular file, nor a " + "directory - skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + + uio_free(fileName); + return 0; +} + +static off_t +zip_findEndOfCentralDirectoryRecord(uio_Handle *handle, + uio_FileBlock *fileBlock) { + off_t fileSize; + off_t endPos, startPos; + char *buf, *bufPtr; + ssize_t bufLen; + + struct stat statBuf; + if (uio_fstat(handle, &statBuf) == -1) { + // errno is set + return -1; + } + fileSize = statBuf.st_size; + startPos = fileSize - 0xffff - 22; // max comment and record size + if (startPos < 0) + startPos = 0; + endPos = fileSize - 22; // last position to be checked + bufLen = uio_accessFileBlock(fileBlock, startPos, endPos - startPos + 4, + &buf); + if (bufLen == -1) { + int savedErrno = errno; + fprintf(stderr, "Error: Read error while searching for " + "'end-of-central-directory record'.\n"); + errno = savedErrno; + return -1; + } + if (bufLen != endPos - startPos + 4) { + fprintf(stderr, "Error: Read error while searching for " + "'end-of-central-directory record'.\n"); + errno = EIO; + return -1; + } + bufPtr = buf + (endPos - startPos); + while (1) { + if (bufPtr < buf) { + fprintf(stderr, "Error: Zip file corrupt; could not find " + "'end-of-central-directory record'.\n"); + errno = EIO; + return -1; + } + if (bufPtr[0] == 0x50 && bufPtr[1] == 0x4b && bufPtr[2] == 0x05 && + bufPtr[3] == 0x06) + break; + bufPtr--; + } + return startPos + (bufPtr - buf); +} + +static mode_t +zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes) { + switch (creatorOS) { + case zip_OSType_FAT: + case zip_OSType_NTFS: + case zip_OSType_VFAT: { + // Only the least signigicant byte is relevant. + mode_t mode; + + if (modeBytes == 0) { + // File came from standard input + return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH; + } + if (modeBytes & 0x10) { + // Directory + mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + } else { + // Regular file + mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + } + if (modeBytes & 0x01) { + // readonly + return mode; + } else { + // Write allowed + return mode | S_IWUSR | S_IWGRP | S_IWOTH; + } + } + case zip_OSType_UNIX: + return (mode_t) (modeBytes >> 16); + default: + fprintf(stderr, "Warning: file created by unknown OS.\n"); + return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + } +} + +// If the data is read from the central directory, certain data +// will need to be updated from the local directory when it is needed. +// This function does that. +// returns 0 for success, -1 for error (errno is set) +// NB: Only the fields that may offer new information are checked. +// the fields that were already read from the central directory +// aren't verified. +static int +zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData, + uio_FileBlock *fileBlock, int pos) { + ssize_t numBytes; + char *buf; + uio_uint32 signature; + uio_uint16 fileNameLength; + uio_uint16 extraFieldLength; + + numBytes = uio_accessFileBlock(fileBlock, pos, 30, &buf); + if (numBytes != 30) { + errno = EIO; + return -1; + } + signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]); + if (signature != 0x04034b50) { + errno = EIO; + return -1; + } + fileNameLength = makeUInt16(buf[26], buf[27]); + extraFieldLength = makeUInt16(buf[28], buf[29]); + pos += 30 + fileNameLength; + + switch (zip_fillDirStructureProcessExtraFields(fileBlock, + extraFieldLength, gPFileData, "<unknown>", pos, false)) { + case 0: // file is ok + break; + case 1: + // File is not acceptable (but according to the central header + // it was) + fprintf(stderr, "Warning: according to the central directory " + "of a zip file, some file inside is acceptable, " + "but according to the local header it isn't.\n"); + errno = EIO; + return -1; + case -1: + errno = EIO; + return -1; + } + pos += extraFieldLength; + gPFileData->fileOffset = pos; + return 0; +} +#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */ + +#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS +static int +zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle) { + uio_FileBlock *fileBlock; + off_t pos; + char *buf; + ssize_t numBytes; + + pos = uio_lseek(handle, 0, SEEK_SET); + if (pos == -1) { + int savedErrno = errno; + errno = savedErrno; + return -1; + } + if (pos != 0) { + errno = EIO; + // Using EIO to report an error in the backend. + return -1; + } + + // We read all the files from the beginning of the zip file to the end. + // (the directory record at the end of the file is ignored) + fileBlock = uio_openFileBlock(handle); + if (fileBlock == NULL) { + // errno is set + return -1; + } + + pos = 0; + while (1) { + uio_uint32 signature; + + numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf); + if (numBytes == -1) + goto err; + if (numBytes != 4) + break; + signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]); + if (signature != 0x04034b50) { + // End of file data reached. + break; + } + pos += 4; + if (zip_fillDirStructureLocalProcessEntry(top, fileBlock, &pos) == -1) + goto err; + } + + uio_closeFileBlock(fileBlock); + return 0; + +err: + { + int savedErrno = errno; + + uio_closeFileBlock(fileBlock); + errno = savedErrno; + return -1; + } +} + +static int +zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir, + uio_FileBlock *fileBlock, off_t *pos) { + char *buf; + zip_GPFileData *gPFileData; + ssize_t numBytes; + + uio_uint16 lastModTime; + uio_uint16 lastModDate; + uio_uint32 crc; + uio_uint16 fileNameLength; + uio_uint16 extraFieldLength; + char *fileName; + + off_t nextEntryOffset; + + numBytes = uio_accessFileBlock(fileBlock, *pos, 26, &buf); + if (numBytes != 26) + return zip_badFile(NULL, NULL); + + gPFileData = zip_GPFileData_new(); + gPFileData->compressionFlags = makeUInt16(buf[2], buf[3]); + gPFileData->compressionMethod = makeUInt16(buf[4], buf[5]); + lastModTime = makeUInt16(buf[6], buf[7]); + lastModDate = makeUInt16(buf[8], buf[9]); + gPFileData->atime = (time_t) 0; + gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime); + gPFileData->ctime = (time_t) 0; + gPFileData->uid = 0; + gPFileData->gid = 0; + gPFileData->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (!isBitSet(gPFileData->compressionFlags, 3)) { + // If bit 3 is not set, this info will be in the data descriptor + // behind the file data. + crc = makeUInt32(buf[10], buf[11], buf[12], buf[13]); + gPFileData->compressedSize = + makeUInt32(buf[14], buf[15], buf[16], buf[17]); + gPFileData->uncompressedSize = + makeUInt32(buf[18], buf[19], buf[20], buf[21]); + } + fileNameLength = makeUInt16(buf[22], buf[23]); + extraFieldLength = makeUInt16(buf[24], buf[25]); + *pos += 26; + nextEntryOffset = *pos + fileNameLength + extraFieldLength + + gPFileData->compressedSize; + if (isBitSet(gPFileData->compressionFlags, 3)) { + // There's a data descriptor present behind the file data. + nextEntryOffset += 16; + } + + if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS || + !zip_compressionMethodSupported[gPFileData->compressionMethod]) { + fprintf(stderr, "Warning: File '%s' is compressed with " + "unsupported method %d - skipped.\n", fileName, + gPFileData->compressionMethod); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + if (gPFileData->compressedSize == (off_t) 0xffffffff || + gPFileData->uncompressedSize == (off_t) 0xffffffff) { + fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + if (isBitSet(gPFileData->compressionFlags, 0)) { + fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName); + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + return 0; + } + + numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf); + if (numBytes != fileNameLength) + return zip_badFile(gPFileData, NULL); + *pos += fileNameLength; + if (buf[fileNameLength - 1] == '/') { + gPFileData->mode |= S_IFDIR; + fileNameLength--; + } else + gPFileData->mode |= S_IFREG; + fileName = uio_malloc(fileNameLength + 1); + memcpy(fileName, buf, fileNameLength); + fileName[fileNameLength] = '\0'; + + switch (zip_fillDirStructureProcessExtraFields(fileBlock, + extraFieldLength, gPFileData, fileName, *pos, false)) { + case 0: // file is ok + break; + case 1: // file is not acceptable - skip file + *pos = nextEntryOffset; + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + case -1: + return zip_badFile(gPFileData, fileName); + } + *pos += extraFieldLength; + + gPFileData->fileOffset = *pos; + + *pos += gPFileData->compressedSize; + if (isBitSet(gPFileData->compressionFlags, 3)) { + // Now comes a data descriptor. + // The PKWare version (which was never used) misses the signature. + // The InfoZip version is used below. + uio_uint32 signature; + + numBytes = uio_accessFileBlock(fileBlock, *pos, 16, &buf); + if (numBytes != 16) + return zip_badFile(gPFileData, fileName); + + signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]); + if (signature != 0x08074b50) + return zip_badFile(gPFileData, fileName); + crc = makeUInt32(buf[4], buf[5], buf[6], buf[7]); + gPFileData->compressedSize = + makeUInt32(buf[8], buf[9], buf[10], buf[11]); + gPFileData->uncompressedSize = + makeUInt32(buf[12], buf[13], buf[14], buf[15]); + } + + if (gPFileData->ctime == (time_t) 0) + gPFileData->ctime = gPFileData->mtime; + + if (gPFileData->atime == (time_t) 0) + gPFileData->atime = gPFileData->mtime; + + if (S_ISREG(gPFileData->mode)) { + if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } + +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found file '%s'.\n", fileName); +#endif + } else if (S_ISDIR(gPFileData->mode)) { + if (fileName[fileNameLength - 1] == '/') + fileName[fileNameLength - 1] = '\0'; + if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found dir '%s'.\n", fileName); +#endif + } else { + fprintf(stderr, "Warning: '%s' is not a regular file, nor a " + "directory - skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + + uio_free(fileName); + return 0; +} +#endif /* zip_USE_HEADERS == zip_USE_LOCAL_HEADERS */ + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +int +zip_updateFileDataFromLocalHeader(uio_Handle *handle, + zip_GPFileData *gPFileData) { + uio_FileBlock *fileBlock; + + fileBlock = uio_openFileBlock(handle); + if (fileBlock == NULL) { + // errno is set + return -1; + } + if (zip_updatePFileDataFromLocalFileHeader(gPFileData, + fileBlock, gPFileData->headerOffset) == -1) { + int savedErrno = errno; + uio_closeFileBlock(fileBlock); + errno = savedErrno; + return -1; + } + if (gPFileData->ctime == (time_t) 0) + gPFileData->ctime = gPFileData->mtime; + if (gPFileData->atime == (time_t) 0) + gPFileData->atime = gPFileData->mtime; + uio_closeFileBlock(fileBlock); + return 0; +} +#endif + +// If the zip file is bad, -1 is returned (no errno set!) +// If the file in the zip file should be skipped, 1 is returned. +// If the file in the zip file is ok, 0 is returned. +static int +zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock, + off_t extraFieldLength, zip_GPFileData *gPFileData, + const char *fileName, off_t pos, uio_bool central) { + off_t posEnd; + uio_uint16 headerID; + ssize_t dataSize; + ssize_t numBytes; + char *buf; + + posEnd = pos + extraFieldLength; + while (pos < posEnd) { + numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf); + if (numBytes != 4) + return -1; + headerID = makeUInt16(buf[0], buf[1]); + dataSize = (ssize_t) makeUInt16(buf[2], buf[3]); + pos += 4; + numBytes = uio_accessFileBlock(fileBlock, pos, dataSize, &buf); + if (numBytes != dataSize) + return -1; + switch(headerID) { + case 0x000d: // 'Unix0' + // fallthrough + case 0x5855: // 'Unix1' + gPFileData->atime = (time_t) makeUInt32( + buf[0], buf[1], buf[2], buf[3]); + gPFileData->mtime = (time_t) makeUInt32( + buf[4], buf[5], buf[6], buf[7]); + if (central) + break; + if (dataSize > 8) { + gPFileData->uid = (uid_t) makeUInt16(buf[8], buf[9]); + gPFileData->gid = (uid_t) makeUInt16(buf[10], buf[11]); + } + // Unix0 has an extra (ignored) field at the end. + break; + case 0x5455: { // 'time' + uio_uint8 flags; + const char *bufPtr; + flags = buf[0]; + bufPtr = buf + 1; + if (isBitSet(flags, 0)) { + // modification time is present + gPFileData->mtime = (time_t) makeUInt32( + bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]); + bufPtr += 4; + } + // The flags field, even in the central header, specifies what + // is present in the local header. + // The central header only contains a field for the mtime + // when it is present in the local header too, and + // never contains fields for other times. + if (central) + break; + if (isBitSet(flags, 1)) { + // modification time is present + gPFileData->atime = (time_t) makeUInt32( + bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]); + bufPtr += 4; + } + // Creation time and possible other times are skipped. + break; + } + case 0x7855: // 'Unix2' + if (central) + break; + gPFileData->uid = (uid_t) makeUInt16(buf[0], buf[1]); + gPFileData->gid = (uid_t) makeUInt16(buf[2], buf[3]); + break; + case 0x756e: { // 'Unix3' + mode_t mode; + mode = (mode_t) makeUInt16(buf[4], buf[5]); + if (!S_ISREG(mode) && !S_ISDIR(mode)) { + fprintf(stderr, "Warning: Skipping '%s'; not a regular " + "file, nor a directory.\n", fileName); + return 1; + } + gPFileData->uid = (uid_t) makeUInt16(buf[10], buf[11]); + gPFileData->gid = (uid_t) makeUInt16(buf[12], buf[13]); + break; + } + default: +#ifdef DEBUG + fprintf(stderr, "Debug: Extra field 0x%04x unsupported, " + "used for file '%s' - ignored.\n", headerID, + fileName); +#endif + break; + } // switch + pos += dataSize; + } // while + if (pos != posEnd) + return -1; + return 0; +} + +static int +zip_badFile(zip_GPFileData *gPFileData, char *fileName) { + fprintf(stderr, "Error: Bad file format for .zip file.\n"); + if (gPFileData != NULL) + zip_GPFileData_delete(gPFileData); + if (fileName != NULL) + uio_free(fileName); + errno = EINVAL; // Is this the best choice? + return -1; +} + +static inline int +zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) { + uio_GPFile *file; + size_t pathLen; + const char *rest; + const char *pathEnd; + const char *start, *end; + char *buf; + + if (path[0] == '/') + path++; + pathLen = strlen(path); + if (path[pathLen - 1] == '/') { + fprintf(stderr, "Warning: '%s' is not a valid file name - skipped.\n", + path); + errno = EISDIR; + return -1; + } + pathEnd = path + pathLen; + + switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) { + case 0: + // The entire path was matched. The last part was not supposed + // to be a dir. + fprintf(stderr, "Warning: '%s' already exists as a dir - " + "skipped.\n", path); + errno = EISDIR; + return -1; + case ENOTDIR: + fprintf(stderr, "Warning: A component to '%s' is not a " + "directory - file skipped.\n", path); + errno = ENOTDIR; + return -1; + case ENOENT: + break; + } + + buf = uio_malloc(pathLen + 1); + getFirstPathComponent(rest, pathEnd, &start, &end); + while (1) { + uio_GPDir *newGPDir; + + if (end == start || (end - start == 1 && start[0] == '.') || + (end - start == 2 && start[0] == '.' && start[1] == '.')) { + fprintf(stderr, "Warning: file '%s' has an invalid path - " + "skipped.\n", path); + uio_free(buf); + errno = EINVAL; + return -1; + } + if (end == pathEnd) { + // This is the last component; it should be the name of the dir. + rest = start; + break; + } + memcpy(buf, start, end - start); + buf[end - start] = '\0'; + newGPDir = uio_GPDir_prepareSubDir(gPDir, buf); + newGPDir->flags |= uio_GPDir_COMPLETE; + // It will be complete when we're done adding + // all files, and it won't be used before that. + uio_GPDir_commitSubDir(gPDir, buf, newGPDir); + + gPDir = newGPDir; + getNextPathComponent(pathEnd, &start, &end); + } + + uio_free(buf); + + file = uio_GPFile_new(gPDir->pRoot, (uio_GPFileExtra) gPFileData, + uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags)); + uio_GPDir_addFile(gPDir, rest, file); + return 0; +} + +static inline int +zip_foundDir(uio_GPDir *gPDir, const char *path, zip_GPDirData *gPDirData) { + size_t pathLen; + const char *pathEnd; + const char *rest; + const char *start, *end; + char *buf; + + if (path[0] == '/') + path++; + pathLen = strlen(path); + pathEnd = path + pathLen; + + switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) { + case 0: + // The dir already exists. Only need to add gPDirData + if (gPDir->extra != NULL) { + fprintf(stderr, "Warning: '%s' is present more than once " + "in the zip file. The last entry will be used.\n", + path); + zip_GPDirData_delete(gPDir->extra); + } + gPDir->extra = gPDirData; + return 0; + case ENOTDIR: + fprintf(stderr, "Warning: A component of '%s' is not a " + "directory - file skipped.\n", path); + errno = ENOTDIR; + return -1; + case ENOENT: + break; + } + + buf = uio_malloc(pathLen + 1); + getFirstPathComponent(rest, pathEnd, &start, &end); + while (start < pathEnd) { + uio_GPDir *newGPDir; + + if (end == start || (end - start == 1 && start[0] == '.') || + (end - start == 2 && start[0] == '.' && start[1] == '.')) { + fprintf(stderr, "Warning: directory '%s' has an invalid path - " + "skipped.\n", path); + uio_free(buf); + errno = EINVAL; + return -1; + } + memcpy(buf, start, end - start); + buf[end - start] = '\0'; + newGPDir = uio_GPDir_prepareSubDir(gPDir, buf); + newGPDir->flags |= uio_GPDir_COMPLETE; + // It will be complete when we're done adding + // all files, and it won't be used before that. + uio_GPDir_commitSubDir(gPDir, buf, newGPDir); + + gPDir = newGPDir; + getNextPathComponent(pathEnd, &start, &end); + } + gPDir->extra = gPDirData; + + uio_free(buf); + return 0; +} + +static int +zip_initZipStream(z_stream *zipStream) { + int retVal; + + zipStream->next_in = Z_NULL; + zipStream->avail_in = 0; + zipStream->zalloc = zip_alloc; + zipStream->zfree = zip_free; + zipStream->opaque = NULL; + retVal = inflateInit2(zipStream, -MAX_WBITS); + // Negative window size means that no zlib header is present. + // This feature is undocumented in zlib, but it's used + // in the minizip program from the zlib contrib dir. + // The absolute value is used as real Window size. + if (retVal != Z_OK) { + switch (retVal) { + case Z_MEM_ERROR: + fprintf(stderr, "Error: Not enough memory available for " + " decompression.\n"); + break; + case Z_VERSION_ERROR: + fprintf(stderr, "Error: Incompatible version problem for " + " decompression.\n"); + break; + default: + fprintf(stderr, "Fatal: unknown error from inflateInit().\n"); + abort(); + } + if (zipStream->msg != NULL) + fprintf(stderr, "ZLib reports: %s\n", zipStream->msg); + errno = EIO; + // Using EIO to report an error in the backend. + return -1; + } + return 0; +} + +static int +zip_unInitZipStream(z_stream *zipStream) { + int retVal; + + retVal = inflateEnd(zipStream); + if (retVal != Z_OK) { + switch (retVal) { + case Z_STREAM_ERROR: + // This means zipStream is bad, which is most likely an + // error in the code using zlib. + fprintf(stderr, "Fatal: internal error using zlib.\n"); + abort(); + break; + default: + fprintf(stderr, "Fatal: unknown error from inflateEnd().\n"); + abort(); + } + if (zipStream->msg != NULL) + fprintf(stderr, "ZLib reports: %s\n", zipStream->msg); + errno = EIO; + // Using EIO to report an error in the backend. + return -1; + } + return 0; +} + +static int +zip_reInitZipStream(z_stream *zipStream) { + int retVal; + + zipStream->next_in = Z_NULL; + zipStream->avail_in = 0; + retVal = inflateReset(zipStream); + if (retVal != Z_OK) { + switch (retVal) { + case Z_STREAM_ERROR: + // This means zipStream is bad, which is most likely an + // error in the code using zlib. + fprintf(stderr, "Fatal: internal error using zlib.\n"); + abort(); + break; + default: + fprintf(stderr, "Fatal: unknown error from inflateInit().\n"); + abort(); + } + if (zipStream->msg != NULL) + fprintf(stderr, "ZLib reports: %s\n", zipStream->msg); + errno = EIO; + // Using EIO to report an error in the backend. + return -1; + } + return 0; +} + + +// Used internally by zlib for allocating memory. +static voidpf +zip_alloc(voidpf opaque, uInt items, uInt size) { + (void) opaque; + return (voidpf) uio_calloc((size_t) items, (size_t) size); +} + +// Used internally by zlib for freeing memory. +static void +zip_free(voidpf opaque, voidpf address) { + (void) opaque; + uio_free((void *) address); +} + +static inline zip_GPFileData * +zip_GPFileData_new(void) { + return zip_GPFileData_alloc(); +} + +static inline void +zip_GPFileData_delete(zip_GPFileData *gPFileData) { + zip_GPFileData_free(gPFileData); +} + +static inline zip_GPFileData * +zip_GPFileData_alloc(void) { + zip_GPFileData *result = uio_malloc(sizeof (zip_GPFileData)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(zip_GPFileData, (void *) result); +#endif + return result; +} + +static inline void +zip_GPFileData_free(zip_GPFileData *gPFileData) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(zip_GPFileData, (void *) gPFileData); +#endif + uio_free(gPFileData); +} + +static inline void +zip_GPDirData_delete(zip_GPDirData *gPDirData) { + zip_GPDirData_free(gPDirData); +} + +static inline void +zip_GPDirData_free(zip_GPDirData *gPDirData) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(zip_GPFileData, (void *) gPDirData); +#endif + uio_free(gPDirData); +} + + + diff --git a/src/libs/uio/zip/zip.h b/src/libs/uio/zip/zip.h new file mode 100644 index 0000000..4b2762f --- /dev/null +++ b/src/libs/uio/zip/zip.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * 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 + * + */ + +typedef struct zip_Handle *uio_NativeHandle; +typedef void *uio_GPRootExtra; +typedef struct zip_GPFileData *uio_GPFileExtra; +typedef struct zip_GPFileData *uio_GPDirExtra; +typedef struct uio_GPDirEntries_Iterator *uio_NativeEntriesContext; + +#define uio_INTERNAL_PHYSICAL + +#include "../gphys.h" +#include "../iointrn.h" +#include "../uioport.h" +#include "../physical.h" +#include "../types.h" +#include "../fileblock.h" + +#include <zlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +// zip_USE_HEADERS determinines what header for files within a .zip file +// is used when building the directory structure. +// Set to 'zip_USE_CENTRAL_HEADERS' to use the central directory header, +// set to 'zip_USE_LOCAL_HEADERS' to use the local file header. +// Central is highly adviced: it uses much less seeking, and hence is much +// faster. +#define zip_USE_HEADERS zip_USE_CENTRAL_HEADERS +#define zip_USE_CENTRAL_HEADERS 1 +#define zip_USE_LOCAL_HEADERS 2 + +#define zip_INCOMPLETE_STAT + // Ignored unless zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS. + // If defined, extra meta-data for files in the .zip archive + // isn't retrieved from the local file header when zip_stat() + // is called. The uid, gid, file mode, and file times may be + // inaccurate. The advantage is that a possibly costly seek and + // read can be avoided. + +typedef struct zip_GPFileData { + off_t compressedSize; + off_t uncompressedSize; + uio_uint16 compressionFlags; + uio_uint16 compressionMethod; +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + off_t headerOffset; // start of the local header for this file +#endif + off_t fileOffset; // start of the compressed data in the .zip file + uid_t uid; + gid_t gid; + mode_t mode; + time_t atime; // access time + time_t mtime; // modification time + time_t ctime; // change time +} zip_GPFileData; + +typedef zip_GPFileData zip_GPDirData; +// TODO: some of the fields from zip_GPFileData are not needed for +// directories. A few bytes could be saved here by making a seperate +// structure. + +typedef struct zip_Handle { + uio_GPFile *file; + z_stream zipStream; + uio_FileBlock *fileBlock; + off_t uncompressedOffset; + // seek location in the uncompressed stream + off_t compressedOffset; + // seek location in the compressed stream, from the start + // of the compressed file +} zip_Handle; + + +uio_PRoot *zip_mount(uio_Handle *handle, int flags); +int zip_umount(struct uio_PRoot *); +uio_Handle *zip_open(uio_PDirHandle *pDirHandle, const char *file, int flags, + mode_t mode); +void zip_close(uio_Handle *handle); +int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode); +int zip_fstat(uio_Handle *handle, struct stat *statBuf); +int zip_stat(uio_PDirHandle *pDirHandle, const char *name, + struct stat *statBuf); +ssize_t zip_read(uio_Handle *handle, void *buf, size_t count); +off_t zip_seek(uio_Handle *handle, off_t offset, int whence); + + + + |