/*
 * 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;
	}
}

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);
}



