summaryrefslogtreecommitdiff
path: root/src/libs/uio/ioaux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio/ioaux.c')
-rw-r--r--src/libs/uio/ioaux.c930
1 files changed, 930 insertions, 0 deletions
diff --git a/src/libs/uio/ioaux.c b/src/libs/uio/ioaux.c
new file mode 100644
index 0000000..3dc0880
--- /dev/null
+++ b/src/libs/uio/ioaux.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// auxiliary functions for use by io.c
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include "ioaux.h"
+#include "iointrn.h"
+#include "uioport.h"
+#include "paths.h"
+
+static int copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf);
+
+/**
+ * Follow a path starting from a specified physical dir for as long as
+ * possible.
+ *
+ * @param[in] startPDirHandle The physical dir to start from.
+ * @param[in] path The path to follow, relative to
+ * 'startPDirHandle'.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] endPDirHandle The physical dir where you end up after
+ * following 'path' for as long as possible. Unmodified if an error
+ * occurs.
+ * @param[out] pathRest '*pathRest' will point into 'path' to the
+ * start the part that was not matched. Unmodified if an error occurs.
+ *
+ * @retval 0 if the complete path was matched
+ * @retval ENOENT if some component (the next one in '*pathRest') didn't
+ * exist.
+ * @retval ENODIR if a component (the next one in '*pathRest') did exist,
+ * but wasn't a dir.
+ *
+ * @note It is allowed to have 'endPDirHandle' point to pDirHandle, but
+ * care should be taken to keep a reference to the original so its
+ * reference counter can be decremented.
+ * @note It is allowed to have 'pathRest' point to 'path'.
+ */
+int
+uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle **endPDirHandle,
+ const char **pathRest) {
+ const char *pathEnd;
+ const char *partStart, *partEnd;
+ char *tempBuf;
+ uio_PDirHandle *pDirHandle;
+ uio_PDirEntryHandle *entry;
+ int retVal;
+
+ uio_PDirHandle_ref(startPDirHandle);
+ pDirHandle = startPDirHandle;
+ tempBuf = uio_malloc(strlen(path) + 1);
+ // XXX: Use a dynamically allocated array when moving to C99.
+ pathEnd = path + pathLen;
+ getFirstPathComponent(path, pathEnd, &partStart, &partEnd);
+ for (;;) {
+ if (partStart == pathEnd) {
+ retVal = 0;
+ break;
+ }
+ memcpy(tempBuf, partStart, partEnd - partStart);
+ tempBuf[partEnd - partStart] = '\0';
+
+ entry = uio_getPDirEntryHandle(pDirHandle, tempBuf);
+ if (entry == NULL) {
+ retVal = ENOENT;
+ break;
+ }
+ if (!uio_PDirEntryHandle_isDir(entry)) {
+ uio_PDirEntryHandle_unref(entry);
+ retVal = ENOTDIR;
+ break;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = (uio_PDirHandle *) entry;
+ getNextPathComponent(pathEnd, &partStart, &partEnd);
+ }
+
+ uio_free(tempBuf);
+ *pathRest = partStart;
+ *endPDirHandle = pDirHandle;
+ return retVal;
+}
+
+/**
+ * Create a directory inside a physical directory. All non-existant
+ * parent directories will be created as well.
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is relative
+ * @param[in] path The path to the directory to create, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[in] mode The access mode for the newly created directories.
+ *
+ * @returns the new (physical) directory, or NULL if an error occurs, in
+ * which case #errno will be set.
+ */
+uio_PDirHandle *
+uio_makePath(uio_PDirHandle *pDirHandle, const char *path, size_t pathLen,
+ mode_t mode) {
+ const char *rest, *start, *end;
+ uio_PDirHandle *(*mkdirFun)(uio_PDirHandle *, const char *, mode_t);
+ char *buf;
+ const char *pathEnd;
+ uio_PDirHandle *newPDirHandle;
+
+ mkdirFun = pDirHandle->pRoot->handler->mkdir;
+ if (mkdirFun == NULL) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ pathEnd = path + pathLen;
+
+ buf = uio_malloc(pathLen + 1);
+ // worst case length
+ // XXX: Use a dynamically allocated array when moving to C99.
+
+ uio_walkPhysicalPath(pDirHandle, path, pathLen, &pDirHandle, &rest);
+ // The reference to the original pDirHandle is still kept
+ // by the calling function; uio_PDirHandle_unref should not
+ // be called for it.
+ // Rest now points into 'path' to the part from where no physical
+ // dir exists.
+
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+
+ newPDirHandle = mkdirFun(pDirHandle, buf, mode);
+ if (newPDirHandle == NULL) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pDirHandle);
+ errno = savedErrno;
+ uio_free(buf);
+ return NULL;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = newPDirHandle;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ uio_free(buf);
+ return pDirHandle;
+}
+
+
+/**
+ * Copy a file from one physical directory to another.
+ * The copy will have the same file permissions as the original.
+ *
+ * @param[in] fromDir The physical directory where the file to copy is
+ * located.
+ * @param[in] fromName The name of the file to copy.
+ * @param[in] toDir The physical directory where to put the copy.
+ * @param[in] toName The name to use for the copy.
+ *
+ * @note It is up to the caller to make any relevant permissions checks.
+ *
+ * @note This function will fail if a file with the name in 'toName' already
+ * exists, leaving the original file intact. If an error occurs during
+ * copying, an attempt is made to remove the file that was to be the
+ * copy.
+ */
+int
+uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName,
+ uio_PDirHandle *toDir, const char *toName) {
+ uio_FileSystemHandler *fromHandler, *toHandler;
+ uio_Handle *fromHandle;
+ uio_Handle *toHandle;
+#define BUFSIZE 0x10000
+ struct stat statBuf;
+ char *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ fromHandler = fromDir->pRoot->handler;
+ toHandler = toDir->pRoot->handler;
+ if (toHandler->write == NULL || fromHandler->fstat == NULL ||
+ toHandler->unlink == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ fromHandle = (fromHandler->open)(fromDir, fromName, O_RDONLY, 0);
+ if (fromHandle == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ if ((fromHandler->fstat)(fromHandle, &statBuf) == -1)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ toHandle = (toHandler->open)(toDir, toName, O_WRONLY | O_CREAT | O_EXCL,
+ statBuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (toHandle == NULL)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ buf = uio_malloc(BUFSIZE);
+ // not allocated on the stack, as this function may be called
+ // from a thread with little stack space.
+ while (1) {
+ numInBuf = (fromHandler->read)(fromHandle, buf, BUFSIZE);
+ if (numInBuf == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do {
+ numWritten = (toHandler->write)(toHandle, bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ uio_free(buf);
+ (toHandler->close)(toHandle);
+ (fromHandler->close)(fromHandle);
+ return 0;
+}
+
+/*
+ * Closes fromHandle if it's not -1.
+ * Closes fromHandle if it's not -1.
+ * Removes 'toName' from 'toDir' if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1, setting errno to 'error'.
+ */
+static int
+copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf) {
+#ifdef DEBUG
+ fprintf(stderr, "Error while copying: %s\n", strerror(error));
+#endif
+
+ if (fromHandle != NULL)
+ (fromHandler->close)(fromHandle);
+
+ if (toHandle != NULL)
+ (toHandler->close)(toHandle);
+
+ if (toName != NULL)
+ (toHandler->unlink)(toDir, toName);
+
+ if (buf != NULL)
+ uio_free(buf);
+
+ errno = error;
+ return -1;
+}
+
+/*
+ * Description: find PDirHandle and MountInfo structures for reading and
+ * writing for a path from a DirHandle.
+ * This can be used for opening a file and creating directories.
+ * Arguments: dirHandle - the directory to which the path is relative.
+ * path - the path to the component that is to be accessed.
+ * The last component does not have to exist.
+ * flags - used to specify what kind of access is requested
+ * Either O_RDONLY, O_RDWR, O_WRONLY. They may be
+ * OR'd with other values accepted by open(). These
+ * are ignored.
+ * XXX: this is no longer true.
+ * TODO: update this doc, and check uio_open() and
+ * perhaps others (uio_mkdir()) for unnecessary
+ * checks on O_CREAT and O_EXCL.
+ * extraFlags - either 0 or uio_GPA_NOWRITE
+ * When 0, the path will be created if it doesn't
+ * exist in the writing location, but does exist
+ * in the reading location. With uio_GPA_NOWRITE, it
+ * won't be created, and -1 will be returned and errno
+ * will be set to ENOENT.
+ * mountInfoReadPtr - pointer to location where the pointer
+ * to the MountInfo structure for the reading location
+ * should be stored.
+ * readPDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for reading should be stored.
+ * readPRootPath - pointer to the location where the pointer
+ * to the physical path to the reading location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * mountInfoWritePtr - pointer to location where the pointer
+ * to the MountInfo structure for the writing location
+ * should be stored.
+ * writePDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for writing should be stored.
+ * NULL if O_RDONLY was specified.
+ * If this is the same dir as the one refered
+ * to by readPDirHandlePtr, the handles will be the
+ * same too.
+ * writePRootPath - pointer to the location where the pointer
+ * to the physical path to the writing location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * restPtr - pointer to the location where a newly created
+ * string with as contents the last component of 'path'
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * Returns: 0 - success
+ * -1 - failure (errno set)
+ * NB: This is the function that would most benefit from
+ * the introduction of LDirs.
+ * This is also the most messy function. It could
+ * use some more comments, or a cleanup (if possible).
+ */
+int
+uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path,
+ int flags, int extraFlags,
+ uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr,
+ char **readPRootPathPtr,
+ uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr,
+ char **writePRootPathPtr,
+ char **restPtr) {
+ char *fullPath; // path from dirHandle with 'path' added
+ const char *pRootPath; // path from the pRoot of a physical tree
+ const char *readPRootPath, *writePRootPath;
+ const char *rest, *readRest;
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ uio_MountTreeItem *readItem, *writeItem;
+ uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle;
+ int retVal;
+ uio_bool entryExists;
+ // Set if the entry pointed to by path exists (including
+ // the last component of the path)
+
+ // 'path' is relative to dirHandle.
+ // Fill 'fullPath' with the absolute path.
+ if (uio_resolvePath(dirHandle, path, strlen(path), &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // Walk the tree of mount points along 'fullPath'.
+ // 'tree' will be the part of the tree where we end up when we can go
+ // no further. tree->pLocs are all the mounts relevant there.
+ // 'rest' will point within 'fullPath' to what is left, after we can
+ // walk the tree of mountpoints no further.
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ readItem = NULL;
+ readPDirHandle = NULL;
+ readPRootPath = NULL;
+ writeItem = NULL;
+ writePDirHandle = NULL;
+ writePRootPath = NULL;
+ readRest = NULL;
+ // Satisfy compiler.
+ entryExists = false;
+ // try all the MountInfo structures in effect for this MountTree
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ // rest points inside fullPath
+ if (retVal == 0) {
+ // Even the last component appeared to be a dir.
+ // As the last component did exist, we don't go on.
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EISDIR;
+ return -1;
+ }
+ // check if this MountTreeItem is suitable for writing
+ if (writeItem == NULL && !uio_mountInfoIsReadOnly(item->mountInfo))
+ writeItem = item;
+ if (strchr(rest, '/') == NULL) {
+ // There's only one dir component that was not matched.
+ uio_PDirEntryHandle *entry;
+
+ // This MountInfo will do for reading, if the file from the last
+ // component is present in this dir.
+ entry = uio_getPDirEntryHandle(pDirHandle, rest);
+ if (entry != NULL) {
+ // 'rest' exists, and it is not a dir, as otherwise
+ // uio_getPDirEntryHandle wouldn't have stopped where it did.
+ uio_PDirEntryHandle_unref(entry);
+ readItem = item;
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ entryExists = true;
+ break;
+ } else {
+ // 'rest' doesn't exist
+ // We're only interested in this dir if we want to write too.
+ if (((flags & O_ACCMODE) != O_RDONLY) && readItem == NULL) {
+ // Keep the first one.
+ readItem = item;
+ assert(readPDirHandle == NULL);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ continue;
+ }
+ }
+ } else {
+ // There is more than one dir component that was not matched.
+ // If the first component of the non-matched part is a file,
+ // stop here and don't check lower dirs.
+ if (retVal == ENOTDIR) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ } // for
+ // readItem is set to the first readItem for which the path completely
+ // exists (including the final component). If there's no such readItem,
+ // it is set to the first path for which the path exists, but without
+ // the final component (entryExists is false in this case). If there's
+ // no such path either, it's set to NULL.
+
+ if (readItem == NULL) {
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // write access is not needed
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ // Don't touch mountInfoWritePtr and writePDirHandlePtr.
+ // they'd be NULL.
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ } else {
+ if (entryExists) {
+ if ((flags & O_CREAT) && (flags & O_EXCL)) {
+ // An entry should be created, but it already exists and
+ // it may not be overwritten.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = EEXIST;
+ return -1;
+ }
+ } else {
+ // Though the path to the entry existed (readPDirHandle is
+ // set to it), the entry itself doesn't, so we can't use it
+ // unless we intend to create it.
+ if (flags & O_CREAT) {
+ // The entry does not exist, but we can create it.
+ // Handled below.
+ } else {
+ // O_CREAT was not specified, so we cannot create
+ // this entry.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ }
+ }
+ if (writeItem == readItem) {
+ // The read directory is usable as write directory too.
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ // writeItem == readItem
+ uio_PDirHandle_ref(readPDirHandle);
+ *writePDirHandlePtr = readPDirHandle;
+ // No copy&paste error, the read PDirHandle is the write
+ // pDirHandle too.
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ }
+ if (writeItem == NULL) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EPERM;
+ // readItem is not NULL, so ENOENT would not be correct here.
+ return -1;
+ }
+
+ // Left is the case where the write location is different from the
+ // read location.
+
+ pRootPath = uio_mountTreeItemRestPath(writeItem, tree->lastComp,
+ fullPath);
+
+ rest = strrchr(pRootPath, '/');
+ // rest points inside fullPath
+ if (rest == NULL) {
+ rest = pRootPath;
+ uio_PDirHandle_ref(writeItem->mountInfo->pDirHandle);
+ writePDirHandle = writeItem->mountInfo->pDirHandle;
+ } else {
+ // There exists no path for a write dir, so it will have to be created.
+ // writeMountInfo indicates the physical tree where it should end up.
+
+ if (extraFlags & uio_GPA_NOWRITE) {
+ // The caller has specified that the path should not be created.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ writePDirHandle = uio_makePath(writeItem->mountInfo->pDirHandle,
+ pRootPath, rest - pRootPath, 0777);
+ if (writePDirHandle == NULL) {
+ int savedErrno;
+ if (errno == ENOSYS) {
+ // mkdir not supported. We want to report that we failed
+ // because of an error in the underlying layer.
+ // EIO sounds like the best choice.
+ errno = EIO;
+ }
+ savedErrno = errno;
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = savedErrno;
+ return -1;
+ }
+ rest++; // skip the '/'
+ }
+
+ if (!entryExists) {
+ // The path to the read dir exists, but the entry itself doesn't.
+ // After we created the write dir, the same thing holds for
+ // the write dir. As it occurs in an earlier MountItem, we'll use
+ // the writeItem (and writePDirHandle) for reading too.
+ readItem = writeItem;
+ uio_PDirHandle_ref(writePDirHandle);
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = writePDirHandle;
+ }
+
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ *writePDirHandlePtr = writePDirHandle;
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(rest);
+ uio_free(fullPath);
+ return 0;
+}
+
+
+/**
+ * Get handles to the (existing) physical dirs that are effective in a
+ * path 'path' relative from 'dirHandle'
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is
+ * relative.
+ * @param[in] path The path to get the physical dirs for, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] resPDirHandles *resPDirHandles is set to the handles to the
+ * (existing) physical dirs that are effective in 'path' (relative to
+ * pDirHandle), or NULL if there are none.
+ * @param[out] resNumPDirHandles The number of PDirHandles found.
+ * @param[out] resItems If 'resItems' != NULL, *resItems is set to the
+ * MountTreeItems belonging to $pDirHandles, or NULL if none were found.
+ *
+ * @retval 0 if everything went ok.
+ * @retval -1 if an error occurred; #errno is set.
+ */
+int
+uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle ***resPDirHandles,
+ int *resNumPDirHandles, uio_MountTreeItem ***resItems) {
+ uio_PDirHandle **pDirHandles;
+ char *fullPath; // path from dirHandle with 'path' added
+ uio_MountTree *tree;
+ const char *rest;
+ uio_MountTreeItem *item, **items;
+ const char *pRootPath; // path from the pRoot of a physical tree
+ int numPDirHandles;
+ int pDirI; // PDirHandle iterator
+
+ // Determine the absolute path from 'path', which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, pathLen, &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ // fill pDirHandles with all the PDirHandles for the path
+ numPDirHandles = uio_mountTreeCountPLocs(tree);
+ pDirHandles = uio_malloc(numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL) {
+ items = uio_malloc(numPDirHandles * sizeof (uio_MountTreeItem *));
+ } else {
+ items = NULL; // satisfy compiler
+ }
+ pDirI = 0;
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ switch (uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest)) {
+ case 0:
+ // complete path was matched
+ pDirHandles[pDirI] = pDirHandle;
+ if (resItems != NULL)
+ items[pDirI] = item;
+ pDirI++;
+ continue;
+ case ENOENT:
+ // some component couldn't be matched
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ case ENOTDIR:
+ // next component was not a dir
+ // Don't look further at other mount Items.
+ uio_PDirHandle_unref(pDirHandle);
+ break;
+ default:
+ assert(false);
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ }
+ break;
+ }
+ numPDirHandles = pDirI;
+
+ uio_free(fullPath);
+
+ *resPDirHandles = uio_realloc(pDirHandles,
+ numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL)
+ *resItems = uio_realloc(items,
+ numPDirHandles * sizeof (uio_MountTreeItem *));
+ *resNumPDirHandles = numPDirHandles;
+
+ return 0;
+}
+
+// returns 0 if the path is valid and exists
+// returns -1 if the path is not valid or does not exist.
+// in this case errno will be set to:
+// ENOENT if some component didn't exist
+// ENOTDIR is some component exists, but is not a dir
+// something else (like EPATHTOOLONG) for internal errors
+// On success, 'resolvedPath' will be set to the absolute path as returned by
+// uio_resolvePath.
+int
+uio_verifyPath(uio_DirHandle *dirHandle, const char *path,
+ char **resolvedPath) {
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ const char *rest;
+ int retVal;
+
+ // TODO: "////", "/somedir//", and "//somedir" are accepted as valid
+
+ // Determine the absolute path from 'path' which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, strlen(path), resolvedPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, *resolvedPath,
+ &tree, &rest);
+
+ if (rest[0] == '\0') {
+ // Complete match. Even if there are no pLocs in effect here
+ // (which can only happen in case the mount Tree is empty and
+ // we're viewing '/').
+ return 0;
+ }
+
+ // Try all the MountInfo structures in effect for this MountTree.
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ const char *pRootPath;
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp,
+ *resolvedPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ uio_PDirHandle_unref(pDirHandle);
+ switch (retVal) {
+ case 0:
+ // Complete match. We're done.
+ return 0;
+ case ENOTDIR:
+ // A component is matched, but not as a dir. Failed.
+ uio_free(*resolvedPath);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ // No match; try the next pLoc.
+ continue;
+ default:
+ // Unknown error. Let's bail out just to be safe.
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Unknown error from "
+ "uio_walkPhysicalPath: %s\n", strerror(retVal));
+#endif
+ uio_free(*resolvedPath);
+ errno = retVal;
+ return -1;
+ }
+ }
+
+ // No match, exit with ENOENT.
+ uio_free(*resolvedPath);
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * Determine the absolute path given a path relative to a given directory.
+ *
+ * @param[in] dirHandle The directory to which 'path' is relative.
+ * @param[in] path The path, relative to 'dirHandle', to make
+ * absolute.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] destPath Filled with a newly allocated string containing
+ * the sought absolute path. It will not contain a '/' as the first
+ * or last character. It should be freed with uio_free().
+ * Unmodified if an error occurs.
+ *
+ * @returns the length of '*destPath', or -1 if an error occurs, in which
+ * case #errno will be set.
+ */
+ssize_t
+uio_resolvePath(uio_DirHandle *dirHandle, const char *path, size_t pathLen,
+ char **destPath) {
+ size_t len;
+ const char *pathEnd, *start, *end;
+ int numUp; // number of ".." dirs still need to be matched.
+ char *buffer;
+ char *endBufPtr;
+ uio_bool absolute;
+
+ absolute = path[0] == '/';
+ pathEnd = path + pathLen;
+
+ // Pass 1: count the amount of space needed
+ len = 0;
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+
+ // The part from 'dirHandle->path' to 'dirHandle->rootEnd' is
+ // always copied (for a valid path). The rest we'll have to count.
+ // (Note the 'rootEnd' in the initialiser of the for loop.)
+ len += (dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+ }
+ if (numUp > 0) {
+ // too many ".."
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (len == 0) {
+ *destPath = uio_malloc(1);
+ (*destPath)[0] = '\0';
+ return 0;
+ }
+
+ // len--; // we don't want a '/' at the start
+ // len++; // for the terminating '\0'
+ buffer = uio_malloc(len);
+
+ // Pass 2: fill the buffer
+ endBufPtr = buffer + len - 1;
+ *endBufPtr = '\0';
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ *destPath = buffer;
+ return len;
+ }
+ }
+ // copy the part from dirHandle->path to dirHandle->rootEnd
+ endBufPtr -= (dirHandle->rootEnd - dirHandle->path);
+ memcpy(endBufPtr, dirHandle->path, dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ // copy (some of) the components from dirHandle->rootEnd on.
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ break;
+ }
+ }
+ }
+
+ *destPath = buffer;
+ return len;
+}
+
+