diff options
Diffstat (limited to 'src/libs/uio/gphys.c')
-rw-r--r-- | src/libs/uio/gphys.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/src/libs/uio/gphys.c b/src/libs/uio/gphys.c new file mode 100644 index 0000000..f25f557 --- /dev/null +++ b/src/libs/uio/gphys.c @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2003 Serge van den Boom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * Nota bene: later versions of the GNU General Public License do not apply + * to this program. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define uio_INTERNAL_PHYSICAL +#define uio_INTERNAL_GPHYS +typedef void *uio_NativeHandle; +typedef void *uio_GPRootExtra; +typedef void *uio_GPDirExtra; +typedef void *uio_GPFileExtra; + +#include <errno.h> +#include <stdio.h> + +#include "gphys.h" +#include "paths.h" +#include "uioport.h" +#ifdef uio_MEM_DEBUG +# include "memdebug.h" +#endif + +static void uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir); +static uio_GPRoot *uio_GPRoot_alloc(void); + +static void uio_GPRoot_free(uio_GPRoot *gPRoot); + +static inline uio_GPDir *uio_GPDir_alloc(void); +void uio_GPDir_delete(uio_GPDir *gPDir); +static inline void uio_GPDir_free(uio_GPDir *gPDir); + +static inline uio_GPFile *uio_GPFile_alloc(void); +void uio_GPFile_delete(uio_GPFile *gPFile); +static inline void uio_GPFile_free(uio_GPFile *gPFile); + +// Call this when you need to edit a file 'dirName' in the GPDir 'gPDir'. +// a new entry is created when necessary. +// uio_gPDirCommitSubDir should be called when you're done with it. +// +// a copy of dirName is made if needed; the caller remains responsible for +// freeing the original +// Allocates a new dir if necessary. +uio_GPDir * +uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName) { + uio_GPDir *subDir; + uio_GPDirEntry *entry; + + entry = uio_GPDirEntries_find(gPDir->entries, dirName); + if (entry != NULL) { + if (uio_GPDirEntry_isDir(entry)) { + // Return existing subdir. + uio_GPDir_ref((uio_GPDir *) entry); + return (uio_GPDir *) entry; + } else { + // There already exists a file with the same name. + // This should not happen within one file system. + fprintf(stderr, "Directory %s shadows file with the same name " + "from the same filesystem.\n", dirName); + } + } + + // return new subdir + subDir = uio_GPDir_new(gPDir->pRoot, NULL, uio_GPDir_DETACHED); + // subDir->ref is initialised at 1 + return subDir; +} + +// call this when you're done with a dir acquired by a call to +// uio_gPDirPrepareSubDir +void +uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName, + uio_GPDir *subDir) { + if (subDir->flags & uio_GPDir_DETACHED) { + // New dir. + // reference to the subDir is passed along to the upDir, + // so subDir->ref should not be changed. + uio_GPDirEntries_add(gPDir->entries, dirName, subDir); + subDir->flags &= ~uio_GPDir_DETACHED; + if (!(subDir->flags & uio_GPDir_PERSISTENT)) { + // Persistent dirs have an extra reference. + uio_GPDir_unref(subDir); + } + } else { + uio_GPDir_unref(subDir); + } +} + +// a copy of fileName is made if needed; the caller remains responsible for +// freeing the original +void +uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName, uio_GPFile *file) { + // A file will never already exist in a dir. There can only be + // one entry in a physical dir with the same name. + uio_GPDirEntries_add(gPDir->entries, fileName, (uio_GPDirEntry *) file); +} + +// Pre: 'fileName' exists in 'gPDir' and is a dir. +void +uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName) { + uio_GPDirEntry *entry; + uio_GPFile *file; + uio_bool retVal; + + entry = uio_GPDirEntries_find(gPDir->entries, fileName); + if (entry == NULL) { + // This means the file has no associated GPFile. + // This can happen when the GPFile structure is only used for caching. + return; + } + + assert(!uio_GPDirEntry_isDir(entry)); + file = (uio_GPFile *) entry; + uio_GPFile_unref(file); + retVal = uio_GPDirEntries_remove(gPDir->entries, fileName); + assert(retVal); +} + +// Pre: 'dirName' exists in 'gPDir' and is a dir. +void +uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName) { + uio_GPDirEntry *entry; + uio_GPDir *subDir; + uio_bool retVal; + + entry = uio_GPDirEntries_find(gPDir->entries, dirName); + if (entry == NULL) { + // This means the directory has no associated GPDir. + // This can happen when the GPDir structure is only used for caching. + return; + } + + assert(uio_GPDirEntry_isDir(entry)); + subDir = (uio_GPDir *) entry; + if (subDir->flags & uio_GPDir_PERSISTENT) { + // Persistent dirs have an extra reference. + uio_GPDir_unref(subDir); + } + retVal = uio_GPDirEntries_remove(gPDir->entries, dirName); + assert(retVal); +} + +void +uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag) { + if (flag) { + gPDir->flags |= uio_GPDir_COMPLETE; + } else + gPDir->flags &= ~uio_GPDir_COMPLETE; +} + +int +uio_GPDir_entryCount(const uio_GPDir *gPDir) { + return uio_GPDirEntries_count(gPDir->entries); +} + +static void +uio_GPDir_access(uio_GPDir *gPDir) { + if (!(gPDir->flags & uio_GPDir_COMPLETE)) + uio_GPDir_fill(gPDir); +} + +// The ref counter for the dir entry is not incremented. +uio_GPDirEntry * +uio_GPDir_getGPDirEntry(uio_GPDir *gPDir, const char *name) { + uio_GPDir_access(gPDir); + return uio_GPDirEntries_find(gPDir->entries, name); +} + +// The ref counter for the dir entry is not incremented. +uio_PDirEntryHandle * +uio_GPDir_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, + const char *name) { + uio_GPDirEntry *gPDirEntry; + + gPDirEntry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (gPDirEntry == NULL) + return NULL; + uio_GPDirEntry_ref(gPDirEntry); + if (uio_GPDirEntry_isDir(gPDirEntry)) { + return (uio_PDirEntryHandle *) uio_PDirHandle_new(pDirHandle->pRoot, + (uio_GPDir *) gPDirEntry); + } else { + return (uio_PDirEntryHandle *) uio_PFileHandle_new(pDirHandle->pRoot, + (uio_GPFile *) gPDirEntry); + } +} + +/* + * Follow a path starting from a specified physical dir as long as possible. + * When you can get no further, 'endGPDir' will be filled in with the dir + * where you ended up, and 'pathRest' will point into the original path. to + * the beginning of the part that was not matched. + * It is allowed to have endGPDir point to gPDir and/or restPath + * point to path when calling this function. + * returns: 0 if the complete path was matched + * ENOENT if some component (the next one) didn't exists + * ENODIR if a component (the next one) exists but isn't a dir + * See also uio_walkPhysicalPath. The difference is that this one works + * directly on the GPDirs and is faster because of that. + */ +int +uio_walkGPPath(uio_GPDir *startGPDir, const char *path, + size_t pathLen, uio_GPDir **endGPDir, const char **pathRest) { + const char *pathEnd; + const char *partStart, *partEnd; + char *tempBuf; + uio_GPDir *gPDir; + uio_GPDirEntry *entry; + int retVal; + + gPDir = startGPDir; + tempBuf = uio_malloc(strlen(path) + 1); + // XXX: Use a dynamically allocated array when moving to C99. + pathEnd = path + pathLen; + getFirstPathComponent(path, pathEnd, &partStart, &partEnd); + while (1) { + if (partStart == pathEnd) { + retVal = 0; + break; + } + memcpy(tempBuf, partStart, partEnd - partStart); + tempBuf[partEnd - partStart] = '\0'; + + entry = uio_GPDir_getGPDirEntry(gPDir, tempBuf); + if (entry == NULL) { + retVal = ENOENT; + break; + } + if (!uio_GPDirEntry_isDir(entry)) { + retVal = ENOTDIR; + break; + } + gPDir = (uio_GPDir *) entry; + getNextPathComponent(pathEnd, &partStart, &partEnd); + } + + uio_free(tempBuf); + *pathRest = partStart; + *endGPDir = gPDir; + return retVal; +} + +uio_GPDirEntries_Iterator * +uio_GPDir_openEntries(uio_PDirHandle *pDirHandle) { + uio_GPDir_access(pDirHandle->extra); + return uio_GPDirEntries_getIterator(pDirHandle->extra->entries); +} + +// the start of 'buf' will be filled with pointers to strings +// those strings are stored elsewhere in buf. +// The function returns the number of strings passed along, or -1 for error. +// If there are no more entries, the last pointer will be NULL. +// (this pointer counts towards the return value) +int +uio_GPDir_readEntries(uio_GPDirEntries_Iterator **iterator, + char *buf, size_t len) { + char *end; + char **start; + int num; + const char *name; + size_t nameLen; + + // buf will be filled like this: + // The start of buf will contain pointers to char *, + // the end will contain the actual char[] that those pointers point to. + // The start and the end will grow towards eachother. + start = (char **) buf; + end = buf + len; + num = 0; + while (!uio_GPDirEntries_iteratorDone(*iterator)) { + name = uio_GPDirEntries_iteratorName(*iterator); + nameLen = strlen(name) + 1; + + // Does this work with systems that need memory access to be + // aligned on a certain number of bytes? + if ((size_t) (sizeof (char *) + nameLen) > + (size_t) (end - (char *) start)) { + // Not enough room to fit the pointer (at the beginning) and + // the string (at the end). + return num; + } + end -= nameLen; + memcpy(end, name, nameLen); + *start = end; + start++; + num++; + *iterator = uio_GPDirEntries_iteratorNext(*iterator); + } + if (sizeof (char *) > (size_t) (end - (char *) start)) { + // not enough room to fit the NULL pointer. + // It will have to be reported seperately the next time. + return num; + } + *start = NULL; + num++; + return num; +} + +void +uio_GPDir_closeEntries(uio_GPDirEntries_Iterator *iterator) { + uio_GPDirEntries_freeIterator(iterator); +} + +void +uio_GPDir_fill(uio_GPDir *gPDir) { + if ((gPDir->flags & uio_GPDir_COMPLETE) && + !(gPDir->flags & uio_GPDir_NOCACHE)) + return; + assert(gPDir->pRoot->extra->ops->fillGPDir != NULL); + gPDir->pRoot->extra->ops->fillGPDir(gPDir); +} + +void +uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot) { + if (gPRoot->extra == NULL) + return; + assert(gPRoot->ops->deleteGPRootExtra != NULL); + gPRoot->ops->deleteGPRootExtra(gPRoot->extra); +} + +void +uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir) { + if (gPDir->extra == NULL) + return; + assert(gPDir->pRoot->extra->ops->deleteGPDirExtra != NULL); + gPDir->pRoot->extra->ops->deleteGPDirExtra(gPDir->extra); +} + +void +uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile) { + if (gPFile->extra == NULL) + return; + assert(gPFile->pRoot->extra->ops->deleteGPFileExtra != NULL); + gPFile->pRoot->extra->ops->deleteGPFileExtra(gPFile->extra); +} + +int +uio_gPDirFlagsFromPRootFlags(int flags) { + int newFlags; + + newFlags = 0; + if (flags & uio_PRoot_NOCACHE) + newFlags |= uio_GPDir_NOCACHE; + + return newFlags; +} + +int +uio_gPFileFlagsFromPRootFlags(int flags) { + int newFlags; + + newFlags = 0; + if (flags & uio_PRoot_NOCACHE) + newFlags |= uio_GPFile_NOCACHE; + + return newFlags; +} + +// This function is to be called from the physical layer. +// uio_GPDirHandle is the extra information for an uio_PDirHandle. +// This is in practice a pointer to the uio_GPDir. +void +uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle) { + uio_GPDir_unref((uio_GPDir *) gPDirHandle); + (void) gPDirHandle; +} + +// This function is to be called from the physical layer. +// uio_GPFileHandle is the extra information for an uio_PFileHandle. +// This is in practice a pointer to the uio_GPFile. +void +uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle) { + uio_GPFile_unref((uio_GPFile *) gPFileHandle); + (void) gPFileHandle; +} + +void +uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry) { + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_GPDir_delete((uio_GPDir *) gPDirEntry); + } else { + uio_GPFile_delete((uio_GPFile *) gPDirEntry); + } +} + +// note: sets ref count to 1 +uio_GPDir * +uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags) { + uio_GPDir *gPDir; + + gPDir = uio_GPDir_alloc(); + gPDir->ref = 1; + gPDir->pRoot = pRoot; + gPDir->entries = uio_GPDirEntries_new(); + gPDir->extra = extra; + flags |= uio_gPDirFlagsFromPRootFlags(gPDir->pRoot->flags); + if (pRoot->extra->flags & uio_GPRoot_PERSISTENT) + flags |= uio_GPDir_PERSISTENT; + gPDir->flags = flags | uio_GPDirEntry_TYPE_DIR; + return gPDir; +} + +// pre: There are no more references to within the gPDir, and +// gPDir is the last reference to the gPDir itself. +void +uio_GPDir_delete(uio_GPDir *gPDir) { +#if 0 + uio_GPDirEntry *entry; + + uio_GPDirEntries_Iterator *iterator; + + iterator = uio_GPDirEntries_getIterator(gPDir->entries); + while (!uio_GPDirEntries_iteratorDone(iterator)) { + entry = uio_GPDirEntries_iteratorItem(iterator); + assert(entry->ref == 0); + if (uio_GPDirEntry_isDir(entry)) { + uio_GPDir_delete((uio_GPDir *) entry); + } else { + uio_GPFile_delete((uio_GPFile *) entry); + } + iterator = uio_GPDirEntries_iteratorNext(iterator); + } +#endif + + assert(gPDir->ref == 0); + uio_GPDirEntries_deleteHashTable(gPDir->entries); + uio_GPDir_deleteGPDirExtra(gPDir); + uio_GPDir_free(gPDir); +} + +static void +uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir) { + uio_GPDirEntry *entry; + uio_GPDirEntries_Iterator *iterator; + + iterator = uio_GPDirEntries_getIterator(gPDir->entries); + while (!uio_GPDirEntries_iteratorDone(iterator)) { + entry = uio_GPDirEntries_iteratorItem(iterator); + if (uio_GPDirEntry_isDir(entry)) { + uio_GPDir_deepPersistentUnref((uio_GPDir *) entry); + } else { + uio_GPFile_unref((uio_GPFile *) entry); + } + iterator = uio_GPDirEntries_iteratorNext(iterator); + } + uio_GPDirEntries_freeIterator(iterator); + if (gPDir->flags & uio_GPDir_PERSISTENT) { + uio_GPDir_unref(gPDir); + } else { + gPDir->flags &= ~uio_GPDir_COMPLETE; + } +} + +static inline uio_GPDir * +uio_GPDir_alloc(void) { + uio_GPDir *result = uio_malloc(sizeof (uio_GPDir)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPDir, (void *) result); +#endif + return result; +} + +static inline void +uio_GPDir_free(uio_GPDir *gPDir) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPDir, (void *) gPDir); +#endif + uio_free(gPDir); +} + +// note: sets ref count to 1 +uio_GPFile * +uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags) { + uio_GPFile *gPFile; + + gPFile = uio_GPFile_alloc(); + gPFile->ref = 1; + gPFile->pRoot = pRoot; + gPFile->extra = extra; + gPFile->flags = flags; + return gPFile; +} + +void +uio_GPFile_delete(uio_GPFile *gPFile) { + assert(gPFile->ref == 0); + uio_GPFile_deleteGPFileExtra(gPFile); + uio_GPFile_free(gPFile); +} + +static inline uio_GPFile * +uio_GPFile_alloc(void) { + uio_GPFile *result = uio_malloc(sizeof (uio_GPFile)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPFile, (void *) result); +#endif + return result; +} + +static inline void +uio_GPFile_free(uio_GPFile *gPFile) { + uio_free(gPFile); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPFile, (void *) gPFile); +#endif +} + +// The ref counter to 'handle' is not incremented. +uio_PRoot * +uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags, + uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags, + uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags) { + uio_PRoot *result; + uio_GPDir *gPTopDir; + uio_GPRoot *gPRoot; + + gPRoot = uio_GPRoot_new(ops, gPRootExtra, gPRootFlags); + result = uio_PRoot_new(NULL, handler, handle, gPRoot, pRootFlags); + + gPTopDir = uio_GPDir_new(result, gPDirExtra, gPDirFlags); + if (gPRoot->flags & uio_GPRoot_PERSISTENT) + uio_GPDir_ref(gPTopDir); + result->rootDir = uio_GPDir_makePDirHandle(gPTopDir); + + return result; +} + +// Pre: there are no more references to PRoot or anything inside it. +int +uio_GPRoot_umount(uio_PRoot *pRoot) { + uio_PDirHandle *topDirHandle; + uio_GPDir *topDir; + + topDirHandle = uio_PRoot_getRootDirHandle(pRoot); + topDir = topDirHandle->extra; + if (pRoot->extra->flags & uio_GPRoot_PERSISTENT) + uio_GPDir_deepPersistentUnref(topDir); + uio_PDirHandle_unref(topDirHandle); + (void) pRoot; + return 0; +} + +uio_GPRoot * +uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra, int flags) { + uio_GPRoot *result; + + result = uio_GPRoot_alloc(); + result->ops = ops; + result->extra = extra; + result->flags = flags; + return result; +} + +void +uio_GPRoot_delete(uio_GPRoot *gPRoot) { + uio_GPRoot_deleteGPRootExtra(gPRoot); + uio_GPRoot_free(gPRoot); +} + +static uio_GPRoot * +uio_GPRoot_alloc(void) { + uio_GPRoot *result = uio_malloc(sizeof (uio_GPRoot)); +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugAlloc(uio_GPRoot, (void *) result); +#endif + return result; +} + +static void +uio_GPRoot_free(uio_GPRoot *gPRoot) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(uio_GPRoot, (void *) gPRoot); +#endif + uio_free(gPRoot); +} + +// The ref counter to the gPDir is not inremented. +uio_PDirHandle * +uio_GPDir_makePDirHandle(uio_GPDir *gPDir) { + return uio_PDirHandle_new(gPDir->pRoot, gPDir); +} + +#ifdef DEBUG +void +uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry) { + if (uio_GPDirEntry_isDir(gPDirEntry)) { + uio_GPDir_print(outStream, (uio_GPDir *) gPDirEntry); + } else { + uio_GPFile_print(outStream, (uio_GPFile *) gPDirEntry); + } +} + +void +uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir) { + (void) outStream; + (void) gPDir; +} + +void +uio_GPFile_print(FILE *outStream, uio_GPFile *gPFile) { + (void) outStream; + (void) gPFile; +} +#endif + + |