Index: example/main.tcl ================================================================== --- example/main.tcl +++ example/main.tcl @@ -1,1 +1,9 @@ -puts "Hello World" +set fd [open "//xvfs:/example/foo"] +seek $fd 0 end +seek $fd -1 current +set check [read $fd 1] +if {$check != "\n"} { + error "EXPECTED: (new line); GOT: [binary encode hex $check]" +} + +puts "ALL TESTS PASSED" Index: xvfs-core.c ================================================================== --- xvfs-core.c +++ xvfs-core.c @@ -1,7 +1,10 @@ #include #include +#include +#include +#include #include #if defined(XVFS_MODE_FLEXIBLE) || defined(XVFS_MODE_SERVER) #define XVFS_INTERNAL_SERVER_MAGIC "\xD4\xF3\x05\x96\x25\xCF\xAF\xFE" #define XVFS_INTERNAL_SERVER_MAGIC_LEN 8 @@ -24,34 +27,38 @@ * Internal Core Utilities */ static const char *xvfs_relativePath(Tcl_Obj *path, struct xvfs_tclfs_instance_info *info) { const char *pathStr, *rootStr; int pathLen, rootLen; - + pathStr = Tcl_GetStringFromObj(path, &pathLen); rootStr = Tcl_GetStringFromObj(info->mountpoint, &rootLen); - + if (pathLen < rootLen) { return(NULL); } - + if (memcmp(pathStr, rootStr, rootLen) != 0) { return(NULL); } - + if (pathLen == rootLen) { return(""); } /* XXX:TODO: Should this use the native OS path separator ? */ if (pathStr[rootLen] != '/') { return(NULL); } - + return(pathStr + rootLen + 1); } +#if 0 +/* + * Currently unused + */ static const char *xvfs_perror(int xvfs_error) { if (xvfs_error >= 0) { return("Not an error"); } @@ -68,56 +75,228 @@ return("Bad address"); default: return("Unknown error"); } } +#endif + +static int xvfs_errorToErrno(int xvfs_error) { + if (xvfs_error >= 0) { + return(0); + } + + switch (xvfs_error) { + case XVFS_RV_ERR_ENOENT: + return(ENOENT); + case XVFS_RV_ERR_EINVAL: + return(EINVAL); + case XVFS_RV_ERR_EISDIR: + return(EISDIR); + case XVFS_RV_ERR_ENOTDIR: + return(ENOTDIR); + case XVFS_RV_ERR_EFAULT: + return(EFAULT); + default: + return(ERANGE); + } +} + +/* + * Xvfs Memory Channel + */ +struct xvfs_tclfs_channel_id { + Tcl_Channel channel; + struct xvfs_tclfs_instance_info *fsInstanceInfo; + Tcl_Obj *path; + Tcl_WideInt currentOffset; + Tcl_WideInt fileSize; +}; +static Tcl_ChannelType xvfs_tclfs_channelType; + +static Tcl_Channel xvfs_tclfs_openChannel(Tcl_Obj *path, struct xvfs_tclfs_instance_info *instanceInfo) { + struct xvfs_tclfs_channel_id *channelInstanceData; + Tcl_Channel channel; + Tcl_StatBuf fileInfo; + int statRet; + + statRet = instanceInfo->fsInfo->getStatProc(Tcl_GetString(path), &fileInfo); + if (statRet < 0) { + return(NULL); + } + + channelInstanceData = (struct xvfs_tclfs_channel_id *) Tcl_Alloc(sizeof(*channelInstanceData)); + channelInstanceData->currentOffset = 0; + channelInstanceData->channel = NULL; + + channelInstanceData->fsInstanceInfo = instanceInfo; + channelInstanceData->path = path; + channelInstanceData->fileSize = fileInfo.st_size; + Tcl_IncrRefCount(path); + + channel = Tcl_CreateChannel(&xvfs_tclfs_channelType, Tcl_GetString(path), channelInstanceData, TCL_READABLE); + if (!channel) { + Tcl_DecrRefCount(path); + Tcl_Free((char *) channelInstanceData); + + return(NULL); + } + + channelInstanceData->channel = channel; + + return(channel); +} + +static int xvfs_tclfs_closeChannel(ClientData channelInstanceData_p, Tcl_Interp *interp) { + struct xvfs_tclfs_channel_id *channelInstanceData; + + channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p; + + Tcl_DecrRefCount(channelInstanceData->path); + Tcl_Free((char *) channelInstanceData); + + return(0); +} + +static int xvfs_tclfs_readChannel(ClientData channelInstanceData_p, char *buf, int bufSize, int *errorCodePtr) { + struct xvfs_tclfs_channel_id *channelInstanceData; + const unsigned char *data; + Tcl_WideInt offset, length; + char *path; + + channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p; + + path = Tcl_GetString(channelInstanceData->path); + offset = channelInstanceData->currentOffset; + length = bufSize; + + data = channelInstanceData->fsInstanceInfo->fsInfo->getDataProc(path, offset, &length); + + if (length < 0) { + *errorCodePtr = xvfs_errorToErrno(length); + + return(-1); + } + + memcpy(buf, data, length); + + channelInstanceData->currentOffset += length; + + return(length); +} + +static void xvfs_tclfs_watchChannel(ClientData channelInstanceData_p, int mask) { + if ((mask & TCL_READABLE) != TCL_READABLE) { + return; + } + +/* XXX:TODO: fprintf(stderr, "Incomplete watch called, mask = %i\n", mask); */ + return; +} + +static int xvfs_tclfs_seekChannel(ClientData channelInstanceData_p, long offset, int mode, int *errorCodePtr) { + struct xvfs_tclfs_channel_id *channelInstanceData; + Tcl_WideInt newOffset, fileSize; + + channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p; + + newOffset = channelInstanceData->currentOffset; + fileSize = channelInstanceData->fileSize; + + switch (mode) { + case SEEK_CUR: + newOffset += offset; + break; + case SEEK_SET: + newOffset = offset; + break; + case SEEK_END: + newOffset = fileSize + offset; + break; + default: + *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL); + + return(-1); + } + + /* + * We allow users to seek right up to the end of the buffer, but + * no further, this way if they want to seek backwards from there + * it is possible to do so. + */ + if (newOffset < 0 || newOffset > fileSize) { + *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL); + + return(-1); + } + + channelInstanceData->currentOffset = newOffset; + + return(channelInstanceData->currentOffset); +} + +static void xvfs_tclfs_prepareChannelType(void) { + xvfs_tclfs_channelType.typeName = "xvfs"; + xvfs_tclfs_channelType.version = TCL_CHANNEL_VERSION_2; + xvfs_tclfs_channelType.closeProc = xvfs_tclfs_closeChannel; + xvfs_tclfs_channelType.inputProc = xvfs_tclfs_readChannel; + xvfs_tclfs_channelType.outputProc = NULL; + xvfs_tclfs_channelType.watchProc = xvfs_tclfs_watchChannel; + xvfs_tclfs_channelType.getHandleProc = NULL; + xvfs_tclfs_channelType.seekProc = xvfs_tclfs_seekChannel; + xvfs_tclfs_channelType.setOptionProc = NULL; + xvfs_tclfs_channelType.getOptionProc = NULL; + xvfs_tclfs_channelType.close2Proc = NULL; + xvfs_tclfs_channelType.blockModeProc = NULL; + xvfs_tclfs_channelType.flushProc = NULL; + xvfs_tclfs_channelType.handlerProc = NULL; + xvfs_tclfs_channelType.wideSeekProc = NULL; + xvfs_tclfs_channelType.threadActionProc = NULL; + xvfs_tclfs_channelType.truncateProc = NULL; +} /* * Internal Tcl_Filesystem functions, with the appropriate instance info */ static int xvfs_tclfs_pathInFilesystem(Tcl_Obj *path, ClientData *dataPtr, struct xvfs_tclfs_instance_info *instanceInfo) { const char *relativePath; - + relativePath = xvfs_relativePath(path, instanceInfo); if (!relativePath) { return(-1); } - + return(TCL_OK); } static int xvfs_tclfs_stat(Tcl_Obj *path, Tcl_StatBuf *statBuf, struct xvfs_tclfs_instance_info *instanceInfo) { const char *pathStr; int retval; pathStr = xvfs_relativePath(path, instanceInfo); - + retval = instanceInfo->fsInfo->getStatProc(pathStr, statBuf); if (retval < 0) { retval = -1; } - + return(retval); } static Tcl_Obj *xvfs_tclfs_listVolumes(struct xvfs_tclfs_instance_info *instanceInfo) { return(NULL); } static Tcl_Channel xvfs_tclfs_openFileChannel(Tcl_Interp *interp, Tcl_Obj *path, int mode, int permissions, struct xvfs_tclfs_instance_info *instanceInfo) { const char *pathStr; - Tcl_WideInt length; - const char *data; pathStr = xvfs_relativePath(path, instanceInfo); - /* - * XXX:TODO: Do something to create the Tcl_Channel we - * need to return here - */ + if (mode & O_WRONLY) { + return(NULL); + } - return(NULL); + return(xvfs_tclfs_openChannel(Tcl_NewStringObj(pathStr, -1), instanceInfo)); } #endif /* XVFS_MODE_SERVER || XVFS_MODE_STANDALONE || XVFS_MODE_FLEIXBLE */ #if defined(XVFS_MODE_STANDALONE) || defined(XVFS_MODE_FLEXIBLE) /* @@ -155,11 +334,11 @@ */ static Tcl_Filesystem xvfs_tclfs_standalone_fs; static int xvfs_standalone_register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) { int tcl_ret; static int registered = 0; - + /* * Ensure this instance is not already registered */ if (registered) { return(TCL_OK); @@ -174,11 +353,11 @@ if (interp) { Tcl_SetResult(interp, "Protocol mismatch", NULL); } return(TCL_ERROR); } - + xvfs_tclfs_standalone_fs.typeName = "xvfs"; xvfs_tclfs_standalone_fs.structureLength = sizeof(xvfs_tclfs_standalone_fs); xvfs_tclfs_standalone_fs.version = TCL_FILESYSTEM_VERSION_1; xvfs_tclfs_standalone_fs.pathInFilesystemProc = xvfs_tclfs_standalone_pathInFilesystem; xvfs_tclfs_standalone_fs.dupInternalRepProc = NULL; @@ -189,11 +368,11 @@ xvfs_tclfs_standalone_fs.filesystemPathTypeProc = NULL; xvfs_tclfs_standalone_fs.filesystemSeparatorProc = NULL; xvfs_tclfs_standalone_fs.statProc = xvfs_tclfs_standalone_stat; xvfs_tclfs_standalone_fs.accessProc = NULL; xvfs_tclfs_standalone_fs.openFileChannelProc = xvfs_tclfs_standalone_openFileChannel; - xvfs_tclfs_standalone_fs.matchInDirectoryProc = NULL; + xvfs_tclfs_standalone_fs.matchInDirectoryProc = NULL; /* XXX:TODO */ xvfs_tclfs_standalone_fs.utimeProc = NULL; xvfs_tclfs_standalone_fs.linkProc = NULL; xvfs_tclfs_standalone_fs.listVolumesProc = xvfs_tclfs_standalone_listVolumes; xvfs_tclfs_standalone_fs.fileAttrStringsProc = NULL; xvfs_tclfs_standalone_fs.fileAttrsGetProc = NULL; @@ -210,20 +389,22 @@ xvfs_tclfs_standalone_fs.chdirProc = NULL; xvfs_tclfs_standalone_info.fsInfo = fsInfo; xvfs_tclfs_standalone_info.mountpoint = Tcl_NewObj(); Tcl_AppendStringsToObj(xvfs_tclfs_standalone_info.mountpoint, XVFS_ROOT_MOUNTPOINT, fsInfo->name, NULL); - + tcl_ret = Tcl_FSRegister(NULL, &xvfs_tclfs_standalone_fs); if (tcl_ret != TCL_OK) { if (interp) { Tcl_SetResult(interp, "Tcl_FSRegister() failed", NULL); } - + return(tcl_ret); } - + + xvfs_tclfs_prepareChannelType(); + return(TCL_OK); } #endif /* XVFS_MODE_STANDALONE || XVFS_MODE_FLEXIBLE */ #if defined(XVFS_MODE_FLEXIBLE) Index: xvfs.c.rvt ================================================================== --- xvfs.c.rvt +++ xvfs.c.rvt @@ -36,12 +36,10 @@ if (path == NULL) { return(XVFS_NAME_LOOKUP_ERROR); } -printf("Looking for %s\n", path); - pathLen = strlen(path); pathHash = Tcl_ZlibAdler32(0, (const unsigned char *) path, pathLen); switch (pathHash) {