summaryrefslogtreecommitdiff
path: root/src/libs/uio/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/uio/utils.c')
-rw-r--r--src/libs/uio/utils.c497
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;
+}
+
+