diff options
Diffstat (limited to 'src/libs/uio/utils.c')
-rw-r--r-- | src/libs/uio/utils.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/src/libs/uio/utils.c b/src/libs/uio/utils.c new file mode 100644 index 0000000..1f705bb --- /dev/null +++ b/src/libs/uio/utils.c @@ -0,0 +1,497 @@ +/* + * 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 <time.h> +#include <stdio.h> +#ifdef _MSC_VER +# include <stdarg.h> +#endif /* _MSC_VER */ + +#include "iointrn.h" +#include "ioaux.h" +#include "utils.h" + +static int uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf); + +struct uio_StdioAccessHandle { + uio_DirHandle *tempRoot; + char *tempDirName; + uio_DirHandle *tempDir; + char *fileName; + char *stdioPath; +}; + +static inline uio_StdioAccessHandle *uio_StdioAccessHandle_new( + uio_DirHandle *tempRoot, char *tempDirName, + uio_DirHandle *tempDir, char *fileName, + char *stdioPath); +static inline void uio_StdioAccessHandle_delete( + uio_StdioAccessHandle *handle); +static inline uio_StdioAccessHandle *uio_StdioAccessHandle_alloc(void); +static inline void uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle); + +/* + * Copy a file with path srcName to a file with name newName. + * If the destination already exists, the operation fails. + * Links are followed. + * Special files (fifos, char devices, block devices, etc) will be + * read as long as there is data available and the destination will be + * a regular file with that data. + * The new file will have the same permissions as the old. + * If an error occurs during copying, an attempt will be made to + * remove the copy. + */ +int +uio_copyFile(uio_DirHandle *srcDir, const char *srcName, + uio_DirHandle *dstDir, const char *newName) { + uio_Handle *src, *dst; + struct stat sb; +#define BUFSIZE 65536 + uio_uint8 *buf, *bufPtr; + ssize_t numInBuf, numWritten; + + src = uio_open(srcDir, srcName, O_RDONLY +#ifdef WIN32 + | O_BINARY +#endif + , 0); + if (src == NULL) + return -1; + + if (uio_fstat(src, &sb) == -1) + return uio_copyError(src, NULL, NULL, NULL, NULL); + + dst = uio_open(dstDir, newName, O_WRONLY | O_CREAT | O_EXCL +#ifdef WIN32 + | O_BINARY +#endif + , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + if (dst == NULL) + return uio_copyError(src, NULL, NULL, NULL, NULL); + + buf = uio_malloc(BUFSIZE); + // This was originally a statically allocated buffer, + // but as this function might be run from a thread with + // a small Stack, this is better. + while (1) { + numInBuf = uio_read(src, buf, BUFSIZE); + if (numInBuf == -1) { + if (errno == EINTR) + continue; + return uio_copyError(src, dst, dstDir, newName, buf); + } + if (numInBuf == 0) + break; + + bufPtr = buf; + do + { + numWritten = uio_write(dst, bufPtr, numInBuf); + if (numWritten == -1) { + if (errno == EINTR) + continue; + return uio_copyError(src, dst, dstDir, newName, buf); + } + numInBuf -= numWritten; + bufPtr += numWritten; + } while (numInBuf > 0); + } + + uio_free(buf); + uio_close(src); + uio_close(dst); + errno = 0; + return 0; +} + +/* + * Closes srcHandle if it's not -1. + * Closes dstHandle if it's not -1. + * Removes unlinkpath from the unlinkHandle dir if it's not NULL. + * Frees 'buf' if not NULL. + * Always returns -1. + * errno is what was before the call. + */ +static int +uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf) { + int savedErrno; + + savedErrno = errno; + +#ifdef DEBUG + fprintf(stderr, "Error while copying: %s\n", strerror(errno)); +#endif + + if (srcHandle != NULL) + uio_close(srcHandle); + + if (dstHandle != NULL) + uio_close(dstHandle); + + if (unlinkPath != NULL) + uio_unlink(unlinkHandle, unlinkPath); + + if (buf != NULL) + uio_free(buf); + + errno = savedErrno; + return -1; +} + +#define NUM_TEMP_RETRIES 16 + // Retry this many times to create a temporary dir, before giving + // up. If undefined, keep trying indefinately. + +uio_StdioAccessHandle * +uio_getStdioAccess(uio_DirHandle *dir, const char *path, int flags, + uio_DirHandle *tempDir) { + int res; + uio_MountHandle *mountHandle; + const char *name; + char *newPath; + char *tempDirName; + uio_DirHandle *newDir; + uio_FileSystemID fsID; + + res = uio_getFileLocation(dir, path, flags, &mountHandle, &newPath); + if (res == -1) { + // errno is set + return NULL; + } + + fsID = uio_getMountFileSystemType(mountHandle); + if (fsID == uio_FSTYPE_STDIO) { + // Current location is usable. + return uio_StdioAccessHandle_new(NULL, NULL, NULL, NULL, newPath); + } + uio_free(newPath); + + { + uio_uint32 dirNum; + int i; + + // Current location is not usable. Create a directory with a + // generated name, as a temporary location to store a copy of + // the file. + dirNum = (uio_uint32) time(NULL); + tempDirName = uio_malloc(sizeof "01234567"); + for (i = 0; ; i++) { +#ifdef NUM_TEMP_RETRIES + if (i >= NUM_TEMP_RETRIES) { + // Using ENOSPC to report that we couldn't create a + // temporary dir, getting EEXIST. + uio_free(tempDirName); + errno = ENOSPC; + return NULL; + } +#endif + + sprintf(tempDirName, "%08lx", (unsigned long) dirNum + i); + + res = uio_mkdir(tempDir, tempDirName, 0700); + if (res == -1) { + int savedErrno; + if (errno == EEXIST) + continue; + savedErrno = errno; +#ifdef DEBUG + fprintf(stderr, "Error: Could not create temporary dir: %s\n", + strerror(errno)); +#endif + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + break; + } + + newDir = uio_openDirRelative(tempDir, tempDirName, 0); + if (newDir == NULL) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not open temporary dir: %s\n", + strerror(errno)); +#endif + res = uio_rmdir(tempDir, tempDirName); +#ifdef DEBUG + if (res == -1) + fprintf(stderr, "Warning: Could not remove temporary dir: " + "%s.\n", strerror(errno)); +#endif + uio_free(tempDirName); + errno = EIO; + return NULL; + } + + // Get the last component of path. This should be the file to + // access. + name = strrchr(path, '/'); + if (name == NULL) + name = path; + + // Copy the file + res = uio_copyFile(dir, path, newDir, name); + if (res == -1) { + int savedErrno = errno; +#ifdef DEBUG + fprintf(stderr, "Error: Could not copy file to temporary dir: " + "%s\n", strerror(errno)); +#endif + uio_closeDir(newDir); + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + } + + res = uio_getFileLocation(newDir, name, flags, &mountHandle, &newPath); + if (res == -1) { + int savedErrno = errno; + fprintf(stderr, "Error: uio_getStdioAccess: Could not get location " + "of temporary dir: %s.\n", strerror(errno)); + uio_closeDir(newDir); + uio_free(tempDirName); + errno = savedErrno; + return NULL; + } + + fsID = uio_getMountFileSystemType(mountHandle); + if (fsID != uio_FSTYPE_STDIO) { + // Temp dir isn't on a stdio fs either. + fprintf(stderr, "Error: uio_getStdioAccess: Temporary file location " + "isn't on a stdio filesystem.\n"); + uio_closeDir(newDir); + uio_free(tempDirName); + uio_free(newPath); +// errno = EXDEV; + errno = EINVAL; + return NULL; + } + + uio_DirHandle_ref(tempDir); + return uio_StdioAccessHandle_new(tempDir, tempDirName, newDir, + uio_strdup(name), newPath); +} + +void +uio_releaseStdioAccess(uio_StdioAccessHandle *handle) { + if (handle->tempDir != NULL) { + if (uio_unlink(handle->tempDir, handle->fileName) == -1) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not remove temporary file: " + "%s\n", strerror(errno)); +#endif + } + + // Need to free this handle in advance. There should be no handles + // to a dir left when removing it. + uio_DirHandle_unref(handle->tempDir); + handle->tempDir = NULL; + + if (uio_rmdir(handle->tempRoot, handle->tempDirName) == -1) { +#ifdef DEBUG + fprintf(stderr, "Error: Could not remove temporary directory: " + "%s\n", strerror(errno)); +#endif + } + } + + uio_StdioAccessHandle_delete(handle); +} + +const char * +uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle) { + return (const char *) handle->stdioPath; +} + +// references to tempRoot and tempDir are not increased. +// no copies of arguments are made. +// By calling this function control of the values is transfered to +// the handle. +static inline uio_StdioAccessHandle * +uio_StdioAccessHandle_new( + uio_DirHandle *tempRoot, char *tempDirName, + uio_DirHandle *tempDir, char *fileName, char *stdioPath) { + uio_StdioAccessHandle *result; + + result = uio_StdioAccessHandle_alloc(); + result->tempRoot = tempRoot; + result->tempDirName = tempDirName; + result->tempDir = tempDir; + result->fileName = fileName; + result->stdioPath = stdioPath; + + return result; +} + +static inline void +uio_StdioAccessHandle_delete(uio_StdioAccessHandle *handle) { + if (handle->tempDir != NULL) + uio_DirHandle_unref(handle->tempDir); + if (handle->fileName != NULL) + uio_free(handle->fileName); + if (handle->tempRoot != NULL) + uio_DirHandle_unref(handle->tempRoot); + if (handle->tempDirName != NULL) + uio_free(handle->tempDirName); + uio_free(handle->stdioPath); + uio_StdioAccessHandle_free(handle); +} + +static inline uio_StdioAccessHandle * +uio_StdioAccessHandle_alloc(void) { + return uio_malloc(sizeof (uio_StdioAccessHandle)); +} + +static inline void +uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle) { + uio_free(handle); +} + +#ifdef _MSC_VER +# include <stdarg.h> +#if 0 /* Unneeded for now */ +// MSVC does not have snprintf(). It does have a _snprintf(), but it does +// not \0-terminate a truncated string as the C standard prescribes. +static inline int +snprintf(char *str, size_t size, const char *format, ...) +{ + int result; + va_list args; + + va_start (args, format); + result = _vsnprintf (str, size, format, args); + if (str != NULL && size != 0) + str[size - 1] = '\0'; + va_end (args); + + return result; +} +#endif + +// MSVC does not have vsnprintf(). It does have a _vsnprintf(), but it does +// not \0-terminate a truncated string as the C standard prescribes. +static inline int +vsnprintf(char *str, size_t size, const char *format, va_list args) +{ + int result = _vsnprintf (str, size, format, args); + if (str != NULL && size != 0) + str[size - 1] = '\0'; + return result; +} +#endif /* _MSC_VER */ + +// The result should be freed using uio_free(). +// NB. POSIX allows errno to be set for vsprintf(), but does not require it: +// "The value of errno may be set to nonzero by a library function call +// whether or not there is an error, provided the use of errno is not +// documented in the description of the function in this International +// Standard." The latter is the case for vsprintf(). +char * +uio_vasprintf(const char *format, va_list args) { + // TODO: If there is a system vasprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + char *buf; + size_t bufSize = 128; + // Start with enough for one screen line, and a power of 2, + // which might give faster result with allocations. + + buf = uio_malloc(bufSize); + if (buf == NULL) { + // errno is set. + return NULL; + } + + for (;;) { + int printResult = vsnprintf(buf, bufSize, format, args); + if (printResult < 0) { + // This means the buffer was not large enough, but vsnprintf() + // does not give us any clue on how large it should be. + // Note that this does not happen with a C'99 compliant + // vsnprintf(), but it will happen on MS Windows, and on + // glibc before version 2.1. + bufSize *= 2; + } else if ((unsigned int) printResult >= bufSize) { + // The buffer was too small, but printResult contains the size + // that the buffer needs to be (excluding the '\0' character). + bufSize = printResult + 1; + } else { + // Success. + if ((unsigned int) printResult + 1 != bufSize) { + // Shorten the resulting buffer to the size that was + // actually needed. + char *newBuf = uio_realloc(buf, printResult + 1); + if (newBuf == NULL) { + // We could have returned the (overly large) original + // buffer, but the unused memory might not be + // acceptable, and the program would be likely to run + // into problems sooner or later anyhow. + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + return newBuf; + } + + return buf; + } + + { + char *newBuf = uio_realloc(buf, bufSize); + if (newBuf == NULL) + { + int savedErrno = errno; + uio_free(buf); + errno = savedErrno; + return NULL; + } + buf = newBuf; + } + } +} + +// As uio_vasprintf(), but with an argument list. +char * +uio_asprintf(const char *format, ...) { + // TODO: If there is a system asprintf, use that. + // XXX: That would mean that the allocation would always go through + // malloc() or so, instead of uio_malloc(), which may not be + // desirable. + + va_list args; + char *result; + int savedErrno; + + va_start(args, format); + result = uio_vasprintf(format, args); + savedErrno = errno; + va_end(args); + + errno = savedErrno; + return result; +} + + |