diff options
Diffstat (limited to 'src/libs/file')
-rw-r--r-- | src/libs/file/Makeinfo | 2 | ||||
-rw-r--r-- | src/libs/file/dirs.c | 830 | ||||
-rw-r--r-- | src/libs/file/files.c | 165 | ||||
-rw-r--r-- | src/libs/file/filintrn.h | 24 | ||||
-rw-r--r-- | src/libs/file/temp.c | 199 |
5 files changed, 1220 insertions, 0 deletions
diff --git a/src/libs/file/Makeinfo b/src/libs/file/Makeinfo new file mode 100644 index 0000000..b4ea11d --- /dev/null +++ b/src/libs/file/Makeinfo @@ -0,0 +1,2 @@ +uqm_CFILES="dirs.c files.c" +uqm_HFILES="filintrn.h" diff --git a/src/libs/file/dirs.c b/src/libs/file/dirs.c new file mode 100644 index 0000000..39a44f5 --- /dev/null +++ b/src/libs/file/dirs.c @@ -0,0 +1,830 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +// Contains code handling directories + +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include "port.h" +#include "config.h" +#include "filintrn.h" +#include "libs/compiler.h" +#include "libs/memlib.h" +#include "libs/misc.h" +#include "libs/log.h" + +#ifdef HAVE_DRIVE_LETTERS +# include <ctype.h> + // For tolower() +#endif /* HAVE_DRIVE_LETTERS */ +#ifdef WIN32 +# include <direct.h> + // For _getdcwd() +#else +# include <pwd.h> + // For getpwuid() +#endif + +/* Try to find a suitable value for %APPDATA% if it isn't defined on + * Windows. + */ +#define APPDATA_FALLBACK + + +static char *expandPathAbsolute (char *dest, size_t destLen, const char *src, + size_t *skipSrc, int what); +static char *strrchr2(const char *start, int c, const char *end); + + +int +createDirectory(const char *dir, int mode) +{ + return MKDIR(dir, mode); +} + +// make all components of the path if they don't exist already +// returns 0 on success, -1 on failure. +// on failure, some parts may still have been created. +int +mkdirhier (const char *path) +{ + char *buf; // buffer + char *ptr; // end of the string in buf + const char *pathstart; // start of a component of path + const char *pathend; // first char past the end of a component of path + size_t len; + struct stat statbuf; + + len = strlen (path); + buf = HMalloc (len + 2); // one extra for possibly added '/' + + ptr = buf; + pathstart = path; + +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(pathstart[0]) && pathstart[1] == ':') + { + // Driveletter + semicolon on Windows. + // Copy as is; don't try to create directories for it. + *(ptr++) = *(pathstart++); + *(ptr++) = *(pathstart++); + + ptr[0] = '/'; + ptr[1] = '\0'; + if (stat (buf, &statbuf) == -1) + { + log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno)); + goto err; + } + } + else +#endif /* HAVE_DRIVE_LETTERS */ +#ifdef HAVE_UNC_PATHS + if (pathstart[0] == '\\' && pathstart[1] == '\\') + { + // Universal Naming Convention path. (\\server\share\...) + // Copy the server part as is; don't try to create directories for + // it, or stat it. Don't create a dir for the share either. + *(ptr++) = *(pathstart++); + *(ptr++) = *(pathstart++); + + // Copy the server part + while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/') + *(ptr++) = *(pathstart++); + + if (*pathstart == '\0') + { + log_add (log_Error, "Incomplete UNC path \"%s\"", pathstart); + goto err; + } + + // Copy the path seperator. + *(ptr++) = *(pathstart++); + + // Copy the share part + while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/') + *(ptr++) = *(pathstart++); + + ptr[0] = '/'; + ptr[1] = '\0'; + if (stat (buf, &statbuf) == -1) + { + log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno)); + goto err; + } + } +#else + { + // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is + // defined. + } +#endif /* HAVE_UNC_PATHS */ + + if (*pathstart == '/') + *(ptr++) = *(pathstart++); + + if (*pathstart == '\0') { + // path exists completely, nothing more to do + goto success; + } + + // walk through the path as long as the components exist + while (1) + { + pathend = strchr (pathstart, '/'); + if (pathend == NULL) + pathend = path + len; + memcpy(ptr, pathstart, pathend - pathstart); + ptr += pathend - pathstart; + *ptr = '\0'; + + if (stat (buf, &statbuf) == -1) + { + if (errno == ENOENT) + break; +#ifdef __SYMBIAN32__ + // XXX: HACK: If we don't have access to a directory, we can + // still have access to the underlying entries. We don't + // actually know whether the entry is a directory, but I know of + // no way to find out. We just pretend that it is; if we were + // wrong, an error will occur when we try to do something with + // the directory. That /should/ not be a problem, as any such + // action should have its own error checking. + if (errno != EACCES) +#endif + { + log_add (log_Error, "Can't stat \"%s\": %s", buf, + strerror (errno)); + goto err; + } + } + + if (*pathend == '\0') + goto success; + + *ptr = '/'; + ptr++; + pathstart = pathend + 1; + while (*pathstart == '/') + pathstart++; + // pathstart is the next non-slash character + + if (*pathstart == '\0') + goto success; + } + + // create all components left + while (1) + { + if (createDirectory (buf, 0777) == -1) + { + log_add (log_Error, "Error: Can't create %s: %s", buf, + strerror (errno)); + goto err; + } + + if (*pathend == '\0') + break; + + *ptr = '/'; + ptr++; + pathstart = pathend + 1; + while (*pathstart == '/') + pathstart++; + // pathstart is the next non-slash character + + if (*pathstart == '\0') + break; + + pathend = strchr (pathstart, '/'); + if (pathend == NULL) + pathend = path + len; + + memcpy (ptr, pathstart, pathend - pathstart); + ptr += pathend - pathstart; + *ptr = '\0'; + } + +success: + HFree (buf); + return 0; + +err: + { + int savedErrno = errno; + HFree (buf); + errno = savedErrno; + } + return -1; +} + +// Get the user's home dir +// returns a pointer to a static buffer from either getenv() or getpwuid(). +const char * +getHomeDir (void) +{ +#ifdef WIN32 + return getenv ("HOME"); +#else + const char *home; + struct passwd *pw; + + home = getenv ("HOME"); + if (home != NULL) + return home; + + pw = getpwuid (getuid ()); + if (pw == NULL) + return NULL; + // NB: pw points to a static buffer. + + return pw->pw_dir; +#endif +} + +// Performs various types of string expansions on a path. +// 'what' is an OR'd compination of the folowing flags, which +// specify what type of exmansions will be performed. +// EP_HOME - Expand '~' for home dirs. +// EP_ABSOLUTE - Make relative paths absolute +// EP_ENVVARS - Expand environment variables +// EP_DOTS - Process ".." and "." +// EP_SLASHES - Consider backslashes as path component separators. +// They will be replaced by slashes. +// EP_SINGLESEP - Replace multiple consecutive path seperators (which POSIX +// considers equivalent to a single one) by a single one. +// Additionally, there's EP_ALL, which indicates all of the above, +// and EP_ALL_SYSTEM, which does the same as EP_ALL, with the exception +// of EP_SLASHES, which will only be included if the operating system +// accepts backslashes as path terminators. +// Returns 0 on success. +// Returns -1 on failure, setting errno. +int +expandPath (char *dest, size_t len, const char *src, int what) +{ + char *destptr, *destend; + char *buf = NULL; + char *bufptr, *bufend; + const char *srcend; + +#define CHECKLEN(bufname, n) \ + if (bufname##ptr + (n) >= bufname##end) \ + { \ + errno = ENAMETOOLONG; \ + goto err; \ + } \ + else \ + (void) 0 + + destptr = dest; + destend = dest + len; + + if (what & EP_ENVVARS) + { + buf = HMalloc (len); + bufptr = buf; + bufend = buf + len; + while (*src != '\0') + { + switch (*src) + { +#ifdef WIN32 + case '%': + { + /* Environment variable substitution in Windows */ + const char *end; // end of env var name in src + const char *envVar; + char *envName; + size_t envNameLen, envVarLen; + + src++; + end = strchr (src, '%'); + if (end == NULL) + { + errno = EINVAL; + goto err; + } + + envNameLen = end - src; + envName = HMalloc (envNameLen + 1); + memcpy (envName, src, envNameLen + 1); + envName[envNameLen] = '\0'; + envVar = getenv (envName); + HFree (envName); + + if (envVar == NULL) + { +#ifdef APPDATA_FALLBACK + if (strncmp (src, "APPDATA", envNameLen) != 0) + { + // Substitute an empty string + src = end + 1; + break; + } + + // fallback for when the APPDATA env var is not set + // Using SHGetFolderPath or SHGetSpecialFolderPath + // is problematic (not everywhere available). + log_add (log_Warning, "Warning: %%APPDATA%% is not set. " + "Falling back to \"%%USERPROFILE%%\\Application " + "Data\""); + envVar = getenv ("USERPROFILE"); + if (envVar != NULL) + { +#define APPDATA_STRING "\\Application Data" + envVarLen = strlen (envVar); + CHECKLEN (buf, + envVarLen + sizeof (APPDATA_STRING) - 1); + strcpy (bufptr, envVar); + bufptr += envVarLen; + strcpy (bufptr, APPDATA_STRING); + bufptr += sizeof (APPDATA_STRING) - 1; + src = end + 1; + break; + } + + // fallback to "./userdata" +#define APPDATA_FALLBACK_STRING ".\\userdata" + log_add (log_Warning, + "Warning: %%USERPROFILE%% is not set. " + "Falling back to \"%s\" for %%APPDATA%%", + APPDATA_FALLBACK_STRING); + CHECKLEN (buf, sizeof (APPDATA_FALLBACK_STRING) - 1); + strcpy (bufptr, APPDATA_FALLBACK_STRING); + bufptr += sizeof (APPDATA_FALLBACK_STRING) - 1; + src = end + 1; + break; + +#else /* !defined (APPDATA_FALLBACK) */ + // Substitute an empty string + src = end + 1; + break; +#endif /* APPDATA_FALLBACK */ + } + + envVarLen = strlen (envVar); + CHECKLEN (buf, envVarLen); + strcpy (bufptr, envVar); + bufptr += envVarLen; + src = end + 1; + break; + } +#endif +#ifndef WIN32 + case '$': + { + const char *end; + char *envName; + size_t envNameLen; + const char *envVar; + size_t envVarLen; + + src++; + if (*src == '{') + { + src++; + end = strchr(src, '}'); + if (end == NULL) + { + errno = EINVAL; + goto err; + } + envNameLen = end - src; + end++; // Skip the '}' + } + else + { + end = src; + while ((*end >= 'A' && *end <= 'Z') || + (*end >= 'a' && *end <= 'z') || + (*end >= '0' && *end <= '9') || + *end == '_') + end++; + envNameLen = end - src; + } + + envName = HMalloc (envNameLen + 1); + memcpy (envName, src, envNameLen + 1); + envName[envNameLen] = '\0'; + envVar = getenv (envName); + HFree (envName); + + if (envVar != NULL) + { + envVarLen = strlen (envVar); + CHECKLEN (buf, envVarLen); + memcpy (bufptr, envVar, envVarLen); + bufptr += envVarLen; + } + + src = end; + break; + } +#endif + default: + CHECKLEN(buf, 1); + *(bufptr++) = *(src++); + break; + } // switch + } // while + *bufptr = '\0'; + src = buf; + srcend = bufptr; + } // if (what & EP_ENVVARS) + else + srcend = src + strlen (src); + + if (what & EP_HOME) + { + if (src[0] == '~') + { + const char *home; + size_t homelen; + + if (src[1] != '/') + { + errno = EINVAL; + goto err; + } + + home = getHomeDir (); + if (home == NULL) + { + errno = ENOENT; + goto err; + } + homelen = strlen (home); + + if (what & EP_ABSOLUTE) { + size_t skip; + destptr = expandPathAbsolute (dest, destend - dest, + home, &skip, what); + if (destptr == NULL) + { + // errno is set + goto err; + } + home += skip; + what &= ~EP_ABSOLUTE; + // The part after the '~' should not be seen + // as absolute. + } + + CHECKLEN (dest, homelen); + memcpy (destptr, home, homelen); + destptr += homelen; + src++; /* skip the ~ */ + } + } + + if (what & EP_ABSOLUTE) + { + size_t skip; + destptr = expandPathAbsolute (destptr, destend - destptr, src, + &skip, what); + if (destptr == NULL) + { + // errno is set + goto err; + } + src += skip; + } + + CHECKLEN (dest, srcend - src); + memcpy (destptr, src, srcend - src + 1); + // The +1 is for the '\0'. It is already taken into account by + // CHECKLEN. + + if (what & EP_SLASHES) + { + /* Replacing backslashes in path by slashes. */ + destptr = dest; +#ifdef HAVE_UNC_PATHS + { + // A UNC path should always start with two backslashes + // and have a backslash in between the server and share part. + size_t skip = skipUNCServerShare (destptr); + if (skip != 0) + { + char *slash = (char *) memchr (destptr + 2, '/', skip - 2); + if (slash) + *slash = '\\'; + destptr += skip; + } + } +#endif /* HAVE_UNC_PATHS */ + while (*destptr != '\0') + { + if (*destptr == '\\') + *destptr = '/'; + destptr++; + } + } + + if (what & EP_DOTS) { + // At this point backslashes are already replaced by slashes if they + // are specified to be path seperators. + // Note that the path can only get smaller, so no size checks + // need to be done. + char *pathStart; + // Start of the first path component, after any + // leading slashes or drive letters. + char *startPart; + char *endPart; + + pathStart = dest; +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(pathStart[0]) && (pathStart[1] == ':')) + { + pathStart += 2; + } + else +#endif /* HAVE_DRIVE_LETTERS */ +#ifdef HAVE_UNC_PATHS + { + // Test for a Universal Naming Convention path. + pathStart += skipUNCServerShare(pathStart); + } +#else + { + // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is + // defined. + } +#endif /* HAVE_UNC_PATHS */ + if (pathStart[0] == '/') + pathStart++; + + startPart = pathStart; + destptr = pathStart; + for (;;) + { + endPart = strchr(startPart, '/'); + if (endPart == NULL) + endPart = startPart + strlen(startPart); + + if (endPart - startPart == 1 && startPart[0] == '.') + { + // Found "." as path component. Ignore this component. + } + else if (endPart - startPart == 2 && + startPart[0] == '.' && startPart[1] == '.') + { + // Found ".." as path component. Remove the previous + // component, and ignore this one. + char *lastSlash; + lastSlash = strrchr2(pathStart, '/', destptr - 1); + if (lastSlash == NULL) + { + if (destptr == pathStart) + { + // We ran out of path components to back out of. + errno = EINVAL; + goto err; + } + destptr = pathStart; + } + else + { + destptr = lastSlash; + if (*endPart == '/') + destptr++; + } + } + else + { + // A normal path component; copy it. + // Using memmove as source and destination may overlap. + memmove(destptr, startPart, endPart - startPart); + destptr += (endPart - startPart); + if (*endPart == '/') + { + *destptr = '/'; + destptr++; + } + } + if (*endPart == '\0') + break; + startPart = endPart + 1; + } + *destptr = '\0'; + } + + if (what & EP_SINGLESEP) + { + char *srcptr; + srcptr = dest; + destptr = dest; + while (*srcptr != '\0') + { + char ch = *srcptr; + *(destptr++) = *(srcptr++); + if (ch == '/') + { + while (*srcptr == '/') + srcptr++; + } + } + *destptr = '\0'; + } + + HFree (buf); + return 0; + +err: + if (buf != NULL) { + int savedErrno = errno; + HFree (buf); + errno = savedErrno; + } + return -1; +} + +#if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE) + // This code is only needed if we have a current working directory + // per drive. +// letter is 0 based: 0 = A, 1 = B, ... +static bool +driveLetterExists(int letter) +{ + unsigned long drives; + + drives = _getdrives (); + + return ((drives >> letter) & 1) != 0; +} +#endif /* if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE) */ + +// helper for expandPath, expanding an absolute path +// returns a pointer to the end of the filled in part of dest. +static char * +expandPathAbsolute (char *dest, size_t destLen, const char *src, + size_t *skipSrc, int what) +{ + const char *orgSrc; + + if (src[0] == '/' || ((what & EP_SLASHES) && src[0] == '\\')) + { + // Path is already absolute; nothing to do + *skipSrc = 0; + return dest; + } + + orgSrc = src; +#ifdef HAVE_DRIVE_LETTERS + if (isDriveLetter(src[0]) && (src[1] == ':')) + { + int letter; + + if (src[2] == '/' || src[2] == '\\') + { + // Path is already absolute (of the form "d:/"); nothing to do + *skipSrc = 0; + return dest; + } + + // Path is of the form "d:path", without a (back)slash after the + // semicolon. + +#ifdef REJECT_DRIVE_PATH_WITHOUT_SLASH + // We reject paths of the form "d:foo/bar". + errno = EINVAL; + return NULL; +#elif defined(HAVE_CWD_PER_DRIVE) + // Paths of the form "d:foo/bar" are treated as "foo/bar" relative + // to the working directory of d:. + letter = tolower(src[0]) - 'a'; + + // _getdcwd() should only be called on drives that exist. + // This is weird though, because it means a race condition + // in between the existance check and the call to _getdcwd() + // cannot be avoided, unless a drive still exists for Windows + // when the physical drive is removed. + if (!driveLetterExists (letter)) + { + errno = ENOENT; + return NULL; + } + + // Get the working directory for a specific drive. + if (_getdcwd (letter + 1, dest, destLen) == NULL) + { + // errno is set + return NULL; + } + + src += 2; +#else /* if !defined(HAVE_CWD_PER_DRIVE) */ + // We treat paths of the form "d:foo/bar" as "d:/foo/bar". + if (destLen < 3) { + errno = ERANGE; + return NULL; + } + dest[0] = src[0]; + dest[1] = ':'; + dest[2] = '/'; + *skipSrc = 2; + dest += 3; + return dest; +#endif /* HAVE_CWD_PER_DRIVE */ + } + else +#endif /* HAVE_DRIVE_LETTERS */ + { + // Relative dir + if (getcwd (dest, destLen) == NULL) + { + // errno is set + return NULL; + } + } + + { + size_t tempLen; + tempLen = strlen (dest); + if (tempLen == 0) + { + // getcwd() or _getdcwd() returned a 0-length string. + errno = ENOENT; + return NULL; + } + dest += tempLen; + destLen -= tempLen; + } + if (dest[-1] != '/' +#ifdef BACKSLASH_IS_PATH_SEPARATOR + && dest[-1] != '\\' +#endif /* BACKSLASH_IS_PATH_SEPARATOR */ + ) + { + // Need to add a slash. + // There's always space, as we overwrite the '\0' that getcwd() + // always returns. + dest[0] = '/'; + dest++; + destLen--; + } + + *skipSrc = (size_t) (src - orgSrc); + return dest; +} + +// As strrchr, but starts searching from the indicated end of the string. +static char * +strrchr2(const char *start, int c, const char *end) { + for (;;) { + end--; + if (end < start) + return (char *) NULL; + if (*end == c) + return (char *) unconst(end); + } +} + +#ifdef HAVE_UNC_PATHS +// returns 0 if the path is not a valid UNC path. +// Does not skip trailing slashes. +size_t +skipUNCServerShare(const char *inPath) { + const char *path = inPath; + + // Skip the initial two backslashes. + if (path[0] != '\\' || path[1] != '\\') + return (size_t) 0; + path += 2; + + // Skip the server part. + while (*path != '\\' && *path != '/') { + if (*path == '\0') + return (size_t) 0; + path++; + } + + // Skip the seperator. + path++; + + // Skip the share part. + while (*path != '\0' && *path != '\\' && *path != '/') + path++; + + return (size_t) (path - inPath); +} +#endif /* HAVE_UNC_PATHS */ + + diff --git a/src/libs/file/files.c b/src/libs/file/files.c new file mode 100644 index 0000000..5ce7dac --- /dev/null +++ b/src/libs/file/files.c @@ -0,0 +1,165 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +// Contains code handling files + +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "port.h" +#include "libs/uio.h" +#include "config.h" +#include "types.h" +#include "filintrn.h" +#include "libs/memlib.h" +#include "libs/log.h" + +static int copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf); + +bool +fileExists (const char *name) +{ + return access (name, F_OK) == 0; +} + +bool +fileExists2(uio_DirHandle *dir, const char *fileName) +{ + uio_Stream *stream; + + stream = uio_fopen (dir, fileName, "rb"); + if (stream == NULL) + return 0; + + uio_fclose (stream); + return 1; +} + +/* + * 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 +copyFile (uio_DirHandle *srcDir, const char *srcName, + uio_DirHandle *dstDir, const char *newName) +{ + uio_Handle *src, *dst; + struct stat sb; +#define BUFSIZE 65536 + 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 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 copyError (src, NULL, NULL, NULL, NULL); + + buf = HMalloc(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 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 copyError (src, dst, dstDir, newName, buf); + } + numInBuf -= numWritten; + bufPtr += numWritten; + } while (numInBuf > 0); + } + + HFree (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 +copyError(uio_Handle *srcHandle, uio_Handle *dstHandle, + uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf) +{ + int savedErrno; + + savedErrno = errno; + + log_add (log_Debug, "Error while copying: %s", strerror (errno)); + + if (srcHandle != NULL) + uio_close (srcHandle); + + if (dstHandle != NULL) + uio_close (dstHandle); + + if (unlinkPath != NULL) + uio_unlink (unlinkHandle, unlinkPath); + + if (buf != NULL) + HFree(buf); + + errno = savedErrno; + return -1; +} + diff --git a/src/libs/file/filintrn.h b/src/libs/file/filintrn.h new file mode 100644 index 0000000..2c6512a --- /dev/null +++ b/src/libs/file/filintrn.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +// Contains code handling temporary files and dirs + +#ifndef _FILEINTRN_H + +#include "../file.h" + +#endif /* _FILEINTRN_H */ + diff --git a/src/libs/file/temp.c b/src/libs/file/temp.c new file mode 100644 index 0000000..3253e0d --- /dev/null +++ b/src/libs/file/temp.c @@ -0,0 +1,199 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +// Contains code handling temporary files and dirs + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#ifdef WIN32 +# include <io.h> +#endif +#include <string.h> +#include <time.h> +#include "filintrn.h" +#include "libs/timelib.h" +#include "port.h" +#include "libs/compiler.h" +#include "libs/log.h" +#include "libs/memlib.h" + +static char *tempDirName; +uio_DirHandle *tempDir; + +static void +removeTempDir (void) +{ + rmdir (tempDirName); +} + +// Try if the null-terminated path 'dir' to a directory is valid +// as temp path. +// On success, 'buf' will be filled with the path, with a trailing /, +// null-terminated, and 0 is returned. +// On failure, EINVAL, ENAMETOOLONG, or one of the errors access() can return +// is returned, and the contents of buf is unspecified. +static int +tryTempDir (char *buf, size_t buflen, const char *dir) +{ + size_t len; + int haveSlash; + + if (dir == NULL) + return EINVAL; + + if (dir[0] == '\0') + return EINVAL; + + len = strlen (dir); + haveSlash = (dir[len - 1] == '/' +#ifdef WIN32 + || dir[len - 1] == '\\' +#endif + ); + if ((haveSlash ? len : len + 1) >= buflen) + return ENAMETOOLONG; + + strcpy (buf, dir); +#if 0 + //def WIN32 + { + char *bufPtr; + for (bufPtr = buf; *bufPtr != '\0'; bufPtr++) + { + if (*bufPtr == '\\') + *bufPtr = '/'; + } + } +#endif + if (!haveSlash) + { + buf[len] = '/'; + len++; + buf[len] = '\0'; + } + if (access (buf, R_OK | W_OK) == -1) + return errno; + + return 0; +} + +static void +getTempDir (char *buf, size_t buflen) { + char cwd[PATH_MAX]; + + if (tryTempDir (buf, buflen, getenv("TMP")) && + tryTempDir (buf, buflen, getenv("TEMP")) && +#if !defined(WIN32) || defined (__CYGWIN__) + tryTempDir (buf, buflen, "/tmp/") && + tryTempDir (buf, buflen, "/var/tmp/") && +#endif + tryTempDir (buf, buflen, getcwd (cwd, sizeof cwd))) + { + log_add (log_Fatal, "Fatal Error: Cannot find a suitable location " + "to store temporary files."); + exit (EXIT_FAILURE); + } +} + +// Sets the global var 'tempDir' +static int +mountTempDir(const char *name) { + static uio_AutoMount *autoMount[] = { NULL }; + uio_MountHandle *tempHandle; + extern uio_Repository *repository; + + tempHandle = uio_mountDir (repository, "/tmp/", + uio_FSTYPE_STDIO, NULL, NULL, name, autoMount, + uio_MOUNT_TOP, NULL); + if (tempHandle == NULL) { + int saveErrno = errno; + log_add (log_Fatal, "Fatal error: Couldn't mount temp dir '%s': " + "%s", name, strerror (errno)); + errno = saveErrno; + return -1; + } + + tempDir = uio_openDir (repository, "/tmp", 0); + if (tempDir == NULL) { + int saveErrno = errno; + log_add (log_Fatal, "Fatal error: Could not open temp dir: %s", + strerror (errno)); + errno = saveErrno; + return -1; + } + return 0; +} + +#define NUM_TEMP_RETRIES 16 + // Number of files to try to open before giving up. +void +initTempDir (void) { + size_t len; + DWORD num; + int i; + char *tempPtr; + // Pointer to the location in the tempDirName string where the + // path to the temp dir ends and the dir starts. + + tempDirName = HMalloc (PATH_MAX); + getTempDir (tempDirName, PATH_MAX - 21); + // reserve 8 chars for dirname, 1 for slash, and 12 for filename + len = strlen(tempDirName); + + num = ((DWORD) time (NULL)); +// num = GetTimeCounter () % 0xffffffff; + tempPtr = tempDirName + len; + for (i = 0; i < NUM_TEMP_RETRIES; i++) + { + sprintf (tempPtr, "%08x", num + i); + if (createDirectory (tempDirName, 0700) == -1) + continue; + + // Success, we've got a temp dir. + tempDirName = HRealloc (tempDirName, len + 9); + atexit (removeTempDir); + if (mountTempDir (tempDirName) == -1) + exit (EXIT_FAILURE); + return; + } + + // Failure, could not make a temporary directory. + log_add (log_Fatal, "Fatal error: Cannot get a name for a temporary " + "directory."); + exit (EXIT_FAILURE); +} + +void +unInitTempDir (void) { + uio_closeDir(tempDir); + // the removing of the dir is handled via atexit +} + +// return the path to a file in the temp dir with the specified filename. +// returns a pointer to a static buffer. +char * +tempFilePath (const char *filename) { + static char file[PATH_MAX]; + + if (snprintf (file, PATH_MAX, "%s/%s", tempDirName, filename) == -1) { + log_add (log_Fatal, "Path to temp file too long."); + exit (EXIT_FAILURE); + } + return file; +} + + |