summaryrefslogtreecommitdiff
path: root/src/libs/uio/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio/io.c')
-rw-r--r--src/libs/uio/io.c1859
1 files changed, 1859 insertions, 0 deletions
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);
+}
+
+
+