diff options
Diffstat (limited to 'erts/emulator/drivers/unix/unix_efile.c')
-rw-r--r-- | erts/emulator/drivers/unix/unix_efile.c | 1505 |
1 files changed, 1505 insertions, 0 deletions
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c new file mode 100644 index 0000000000..d395b68691 --- /dev/null +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -0,0 +1,1505 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Purpose: Provides file and directory operations for Unix. + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "sys.h" +#include "erl_driver.h" +#include "erl_efile.h" +#include <utime.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/types.h> +#include <sys/uio.h> +#endif + +#ifdef _OSE_ +#include "efs.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifdef _OSE_SFK_ +#include <string.h> +#endif +#endif /* _OSE_ */ + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define DARWIN 1 +#endif + +#ifdef DARWIN +#include <fcntl.h> +#endif /* DARWIN */ + +#ifdef VXWORKS +#include <ioLib.h> +#include <dosFsLib.h> +#include <nfsLib.h> +#include <sys/stat.h> +/* +** Not nice to include usrLib.h as MANY normal variable names get reported +** as shadowing globals, like 'i' for example. +** Instead we declare the only function we use here +*/ +/* + * #include <usrLib.h> + */ +extern STATUS copy(char *, char *); +#include <errno.h> +#endif + +#ifdef SUNOS4 +# define getcwd(buf, size) getwd(buf) +#endif + +/* Find a definition of MAXIOV, that is used in the code later. */ +#if defined IOV_MAX +#define MAXIOV IOV_MAX +#elif defined UIO_MAXIOV +#define MAXIOV UIO_MAXIOV +#else +#define MAXIOV 16 +#endif + + +/* + * Macros for testing file types. + */ + +#ifdef _OSE_ + +#define ISDIR(st) S_ISDIR(((st).st_mode)) +#define ISREG(st) S_ISREG(((st).st_mode)) +#define ISDEV(st) (S_ISCHR(((st).st_mode)) || S_ISBLK(((st).st_mode))) +#define ISLNK(st) S_ISLNK(((st).st_mode)) +#ifdef NO_UMASK +#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) +#define DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) +#else +#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#define DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | \ + S_IWOTH | S_IXOTH) +#endif + +#else /* !_OSE_ */ + +#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR) +#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG) +#define ISDEV(st) \ + (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK) +#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK) +#ifdef NO_UMASK +#define FILE_MODE 0644 +#define DIR_MODE 0755 +#else +#define FILE_MODE 0666 +#define DIR_MODE 0777 +#endif + +#endif /* _OSE_ */ + +#ifdef VXWORKS /* Currently only used on vxworks */ + +#define EF_ALLOC(S) driver_alloc((S)) +#define EF_REALLOC(P, S) driver_realloc((P), (S)) +#define EF_SAFE_ALLOC(S) ef_safe_alloc((S)) +#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S)) +#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0) + +extern void erl_exit(int n, char *fmt, _DOTS_); + +static void *ef_safe_alloc(Uint s) +{ + void *p = EF_ALLOC(s); + if (!p) erl_exit(1, + "unix efile drv: Can't allocate %d bytes of memory\n", + s); + return p; +} + +#if 0 /* Currently not used */ + +static void *ef_safe_realloc(void *op, Uint s) +{ + void *p = EF_REALLOC(op, s); + if (!p) erl_exit(1, + "unix efile drv: Can't reallocate %d bytes of memory\n", + s); + return p; +} + +#endif /* #if 0 */ +#endif /* #ifdef VXWORKS */ + +#define IS_DOT_OR_DOTDOT(s) \ + (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0'))) + +#ifdef VXWORKS +static FUNCTION(int, vxworks_to_posix, (int vx_errno)); +#endif + +/* +** VxWorks (not) strikes again. Too long RESULTING paths +** may give the infamous bus error. Have to check ALL +** filenames and pathnames. No wonder the emulator is slow on +** these cards... +*/ +#ifdef VXWORKS +#define CHECK_PATHLEN(Name, ErrInfo) \ + if (path_size(Name) > PATH_MAX) { \ + errno = ENAMETOOLONG; \ + return check_error(-1, ErrInfo); \ + } +#else +#define CHECK_PATHLEN(X,Y) /* Nothing */ +#endif + +static FUNCTION(int, check_error, (int result, Efile_error* errInfo)); + +static int +check_error(int result, Efile_error *errInfo) +{ + if (result < 0) { +#ifdef VXWORKS + errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno); +#else + errInfo->posix_errno = errInfo->os_errno = errno; +#endif + return 0; + } + return 1; +} + +#ifdef VXWORKS + +/* + * VxWorks has different error codes for different file systems. + * We map those to POSIX ones. + */ +static int +vxworks_to_posix(int vx_errno) +{ + DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno)); + switch (vx_errno) { + /* dosFsLib mapping */ +#ifdef S_dosFsLib_FILE_ALREADY_EXISTS + case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST; +#else + case S_dosFsLib_FILE_EXISTS: return EEXIST; +#endif +#ifdef S_dosFsLib_BAD_DISK + case S_dosFsLib_BAD_DISK: return EIO; +#endif +#ifdef S_dosFsLib_CANT_CHANGE_ROOT + case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL; +#endif +#ifdef S_dosFsLib_NO_BLOCK_DEVICE + case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK; +#endif +#ifdef S_dosFsLib_BAD_SEEK + case S_dosFsLib_BAD_SEEK: return ESPIPE; +#endif + case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO; + case S_dosFsLib_DISK_FULL: return ENOSPC; + case S_dosFsLib_FILE_NOT_FOUND: return ENOENT; + case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE; + case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL; + case S_dosFsLib_ILLEGAL_NAME: return EINVAL; + case S_dosFsLib_CANT_DEL_ROOT: return EACCES; + case S_dosFsLib_NOT_FILE: return EISDIR; + case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR; + case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV; + case S_dosFsLib_READ_ONLY: return EACCES; + case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC; + case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST; + case S_dosFsLib_NO_LABEL: return ENXIO; + case S_dosFsLib_INVALID_PARAMETER: return EINVAL; + case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC; + case S_dosFsLib_FD_OBSOLETE: return EBADF; + case S_dosFsLib_DELETED: return EINVAL; + case S_dosFsLib_INTERNAL_ERROR: return EIO; + case S_dosFsLib_WRITE_ONLY: return EACCES; + /* nfsLib mapping - is needed since Windriver has used */ + /* inconsistent error codes (errno.h/nfsLib.h). */ + case S_nfsLib_NFS_OK: return 0; + case S_nfsLib_NFSERR_PERM: return EPERM; + case S_nfsLib_NFSERR_NOENT: return ENOENT; + case S_nfsLib_NFSERR_IO: return EIO; + case S_nfsLib_NFSERR_NXIO: return ENXIO; +#ifdef S_nfsLib_NFSERR_ACCES + case S_nfsLib_NFSERR_ACCES: return EACCES; +#else + case S_nfsLib_NFSERR_ACCESS: return EACCES; +#endif + case S_nfsLib_NFSERR_EXIST: return EEXIST; + case S_nfsLib_NFSERR_NODEV: return ENODEV; + case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR; + case S_nfsLib_NFSERR_ISDIR: return EISDIR; + case S_nfsLib_NFSERR_FBIG: return EFBIG; + case S_nfsLib_NFSERR_NOSPC: return ENOSPC; + case S_nfsLib_NFSERR_ROFS: return EROFS; + case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG; + case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST; + case S_nfsLib_NFSERR_DQUOT: return ENOSPC; + case S_nfsLib_NFSERR_STALE: return EINVAL; + case S_nfsLib_NFSERR_WFLUSH: return ENXIO; + /* And sometimes (...) the error codes are from ioLib (as in the */ + /* case of the (for nfsLib) unimplemented rename function) */ + case S_ioLib_DISK_NOT_PRESENT: return EIO; +#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER + case S_ioLib_NO_DRIVER: return ENXIO; +#endif + case S_ioLib_UNKNOWN_REQUEST: return ENOSYS; + case S_ioLib_DEVICE_TIMEOUT: return EIO; +#ifdef S_ioLib_UNFORMATED + /* Added (VxWorks 5.2 -> 5.3.1) */ + #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT + case S_ioLib_UNFORMATED: return EIO; + #endif +#endif +#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR + case S_ioLib_DEVICE_ERROR: return ENXIO; +#endif + case S_ioLib_WRITE_PROTECTED: return EACCES; + case S_ioLib_NO_FILENAME: return EINVAL; + case S_ioLib_CANCELLED: return EINTR; + case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL; + case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG; +#ifdef S_objLib_OBJ_UNAVAILABLE + case S_objLib_OBJ_UNAVAILABLE: return ENOENT; +#endif + + /* Temporary workaround for a weird error in passFs + (VxWorks Simsparc only). File operation fails because of + ENOENT, but errno is not set. */ +#ifdef SIMSPARCSOLARIS + case 0: return ENOENT; +#endif + + } + /* If the error code matches none of the above, assume */ + /* it is a POSIX one already. The upper bits (>=16) are */ + /* cleared since VxWorks uses those bits to indicate in */ + /* what module the error occured. */ + return vx_errno & 0xffff; +} + +static int +vxworks_enotsup(Efile_error *errInfo) +{ + errInfo->posix_errno = errInfo->os_errno = ENOTSUP; + return 0; +} + +static int +count_path_length(char *pathname, char *pathname2) +{ + static int stack[PATH_MAX / 2 + 1]; + int sp = 0; + char *tmp; + char *cpy = NULL; + int i; + int sum; + for(i = 0;i < 2;++i) { + if (!i) { + cpy = EF_SAFE_ALLOC(strlen(pathname)+1); + strcpy(cpy, pathname); + } else if (pathname2 != NULL) { + EF_FREE(cpy); + cpy = EF_SAFE_ALLOC(strlen(pathname2)+1); + strcpy(cpy, pathname2); + } else + break; + + for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) { + if (!strcmp(tmp,"..") && sp > 0) + --sp; + else if (strcmp(tmp,".")) + stack[sp++] = strlen(tmp); + } + } + if (cpy != NULL) + EF_FREE(cpy); + sum = 0; + for(i = 0;i < sp; ++i) + sum += stack[i]+1; + return (sum) ? sum : 1; +} + +static int +path_size(char *pathname) +{ + static char currdir[PATH_MAX+2]; + if (*pathname == '/') + return count_path_length(pathname,NULL); + ioDefPathGet(currdir); + strcat(currdir,"/"); + return count_path_length(currdir,pathname); +} + +#endif /* VXWORKS */ + +#ifdef _OSE_ +static int +ose_enotsup(Efile_error *errInfo) +{ + errInfo->posix_errno = errInfo->os_errno = ENOTSUP; + return 0; +} +#endif /* _OSE_ */ + +int +efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to create. */ +{ + CHECK_PATHLEN(name,errInfo); +#ifdef NO_MKDIR_MODE +#ifdef VXWORKS + /* This is a VxWorks/nfs workaround for erl_tar to create + * non-existant directories. (of some reason (...) VxWorks + * returns, the *non-module-prefixed*, 0xd code when + * trying to create a directory in a directory that doesn't exist). + * (see efile_openfile) + */ + if (mkdir(name) < 0) { + struct stat sb; + if (name[0] == '\0') { + /* Return the correct error code enoent */ + errno = S_nfsLib_NFSERR_NOENT; + } else if (stat(name, &sb) == OK) { + errno = S_nfsLib_NFSERR_EXIST; + } else if((strchr(name, '/') != NULL) && (errno == 0xd)) { + /* Return the correct error code enoent */ + errno = S_nfsLib_NFSERR_NOENT; + } + return check_error(-1, errInfo); + } else return 1; +#else + return check_error(mkdir(name), errInfo); +#endif +#else + return check_error(mkdir(name, DIR_MODE), errInfo); +#endif +} + +int +efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to delete. */ +{ + CHECK_PATHLEN(name, errInfo); + if (rmdir(name) == 0) { + return 1; + } +#ifdef VXWORKS + if (name[0] == '\0') { + /* Return the correct error code enoent */ + errno = S_nfsLib_NFSERR_NOENT; + } +#else + if (errno == ENOTEMPTY) { + errno = EEXIST; + } + if (errno == EEXIST) { + int saved_errno = errno; + struct stat file_stat; + struct stat cwd_stat; + + /* + * The error code might be wrong if this is the current directory. + */ + + if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 && + file_stat.st_ino == cwd_stat.st_ino && + file_stat.st_dev == cwd_stat.st_dev) { + saved_errno = EINVAL; + } + errno = saved_errno; + } +#endif + return check_error(-1, errInfo); +} + +int +efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of file to delete. */ +{ + CHECK_PATHLEN(name,errInfo); +#ifdef _OSE_ + if (remove(name) == 0) { + return 1; + } +#else + if (unlink(name) == 0) { + return 1; + } + if (errno == EISDIR) { /* Linux sets the wrong error code. */ + errno = EPERM; + } +#endif + return check_error(-1, errInfo); +} + +/* + *--------------------------------------------------------------------------- + * + * Changes the name of an existing file or directory, from src to dst. + * If src and dst refer to the same file or directory, does nothing + * and returns success. Otherwise if dst already exists, it will be + * deleted and replaced by src subject to the following conditions: + * If src is a directory, dst may be an empty directory. + * If src is a file, dst may be a file. + * In any other situation where dst already exists, the rename will + * fail. + * + * Results: + * If the directory was successfully created, returns 1. + * Otherwise the return value is 0 and errno is set to + * indicate the error. Some possible values for errno are: + * + * EACCES: src or dst parent directory can't be read and/or written. + * EEXIST: dst is a non-empty directory. + * EINVAL: src is a root directory or dst is a subdirectory of src. + * EISDIR: dst is a directory, but src is not. + * ENOENT: src doesn't exist, or src or dst is "". + * ENOTDIR: src is a directory, but dst is not. + * EXDEV: src and dst are on different filesystems. + * + * Side effects: + * The implementation of rename may allow cross-filesystem renames, + * but the caller should be prepared to emulate it with copy and + * delete if errno is EXDEV. + * + *--------------------------------------------------------------------------- + */ + +int +efile_rename(Efile_error* errInfo, /* Where to return error codes. */ + char* src, /* Original name. */ + char* dst) /* New name. */ +{ + CHECK_PATHLEN(src,errInfo); + CHECK_PATHLEN(dst,errInfo); +#ifdef VXWORKS + + /* First check if src == dst, if so, just return. */ + /* VxWorks dos file system destroys the file otherwise, */ + /* VxWorks nfs file system rename doesn't work at all. */ + if(strcmp(src, dst) == 0) + return 1; +#endif + if (rename(src, dst) == 0) { + return 1; + } +#ifdef VXWORKS + /* nfs for VxWorks doesn't support rename. We try to emulate it */ + /* (by first copying src to dst and then deleting src). */ + if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned + by ioLib (!) */ + copy(src, dst) == OK && + unlink(src) == OK) + return 1; +#endif + if (errno == ENOTEMPTY) { + errno = EEXIST; + } +#if defined (sparc) && !defined(VXWORKS) && !defined(_OSE_) + /* + * SunOS 4.1.4 reports overwriting a non-empty directory with a + * directory as EINVAL instead of EEXIST (first rule out the correct + * EINVAL result code for moving a directory into itself). Must be + * conditionally compiled because realpath() is only defined on SunOS. + */ + + if (errno == EINVAL) { + char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN]; + DIR *dirPtr; + struct dirent *dirEntPtr; + +#ifdef PURIFY + memset(srcPath, '\0', sizeof(srcPath)); + memset(dstPath, '\0', sizeof(dstPath)); +#endif + + if ((realpath(src, srcPath) != NULL) + && (realpath(dst, dstPath) != NULL) + && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) { + dirPtr = opendir(dst); + if (dirPtr != NULL) { + while ((dirEntPtr = readdir(dirPtr)) != NULL) { + if ((strcmp(dirEntPtr->d_name, ".") != 0) && + (strcmp(dirEntPtr->d_name, "..") != 0)) { + errno = EEXIST; + closedir(dirPtr); + return check_error(-1, errInfo); + } + } + closedir(dirPtr); + } + } + errno = EINVAL; + } +#endif /* sparc */ + + if (strcmp(src, "/") == 0) { + /* + * Alpha reports renaming / as EBUSY and Linux reports it as EACCES, + * instead of EINVAL. + */ + + errno = EINVAL; + } + + /* + * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a + * file across filesystems and the parent directory of that file is + * not writable. Most other systems return EXDEV. Does nothing to + * correct this behavior. + */ + + return check_error(-1, errInfo); +} + +int +efile_chdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name) /* Name of directory to make current. */ +{ + CHECK_PATHLEN(name, errInfo); + return check_error(chdir(name), errInfo); +} + + +int +efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */ + int drive, /* 0 - current, 1 - A, 2 - B etc. */ + char* buffer, /* Where to return the current + directory. */ + size_t size) /* Size of buffer. */ +{ + if (drive == 0) { + if (getcwd(buffer, size) == NULL) + return check_error(-1, errInfo); + +#ifdef SIMSPARCSOLARIS + /* We get "host:" prepended to the dirname - remove!. */ + { + int i = 0; + int j = 0; + while ((buffer[i] != ':') && (buffer[i] != '\0')) i++; + if (buffer[i] == ':') { + i++; + while ((buffer[j++] = buffer[i++]) != '\0'); + } + } +#endif + return 1; + } + + /* + * Drives other than 0 is not supported on Unix. + */ + + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +int +efile_readdir(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory + handle of + open directory.*/ + char* buffer, /* Pointer to buffer for + one filename. */ + size_t size) /* Size of buffer. */ +{ + DIR *dp; /* Pointer to directory structure. */ + struct dirent* dirp; /* Pointer to directory entry. */ + + /* + * If this is the first call, we must open the directory. + */ + + CHECK_PATHLEN(name, errInfo); + + if (*p_dir_handle == NULL) { + dp = opendir(name); + if (dp == NULL) + return check_error(-1, errInfo); + *p_dir_handle = (EFILE_DIR_HANDLE) dp; + } + + /* + * Retrieve the name of the next file using the directory handle. + */ + + dp = *((DIR **)((void *)p_dir_handle)); + for (;;) { + dirp = readdir(dp); + if (dirp == NULL) { + closedir(dp); + return 0; + } + if (IS_DOT_OR_DOTDOT(dirp->d_name)) + continue; + buffer[0] = '\0'; + strncat(buffer, dirp->d_name, size-1); + return 1; + } +} + +int +efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ + char* name, /* Name of directory to open. */ + int flags, /* Flags to user for opening. */ + int* pfd, /* Where to store the file + descriptor. */ + Sint64 *pSize) /* Where to store the size of the + file. */ +{ + struct stat statbuf; + int fd; + int mode; /* Open mode. */ +#ifdef VXWORKS + char pathbuff[PATH_MAX+2]; + char sbuff[PATH_MAX*2]; + char *totbuff = sbuff; + int nameneed; +#endif + + + CHECK_PATHLEN(name, errInfo); + +#ifdef VXWORKS + /* Have to check that it's not a directory. */ + if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) { + errno = EISDIR; + return check_error(-1, errInfo); + } +#endif + + if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) { +#if !defined(VXWORKS) && !defined(OSE) + /* + * For UNIX only, here is some ugly code to allow + * /dev/null to be opened as a file. + * + * Assumption: The i-node number for /dev/null cannot be zero. + */ + static ino_t dev_null_ino = 0; + + if (dev_null_ino == 0) { + struct stat nullstatbuf; + + if (stat("/dev/null", &nullstatbuf) >= 0) { + dev_null_ino = nullstatbuf.st_ino; + } + } + if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) { +#endif + errno = EISDIR; + return check_error(-1, errInfo); +#if !defined(VXWORKS) && !defined(OSE) + } +#endif + } + + switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) { + case EFILE_MODE_READ: + mode = O_RDONLY; + break; + case EFILE_MODE_WRITE: + if (flags & EFILE_NO_TRUNCATE) + mode = O_WRONLY | O_CREAT; + else + mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFILE_MODE_READ_WRITE: + mode = O_RDWR | O_CREAT; + break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + + + if (flags & EFILE_MODE_APPEND) { + mode &= ~O_TRUNC; +#ifndef VXWORKS + mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */ +#endif + } + + +#ifdef VXWORKS + if (*name != '/') { + /* Make sure it is an absolute pathname, because ftruncate needs it */ + ioDefPathGet(pathbuff); + strcat(pathbuff,"/"); + nameneed = strlen(pathbuff) + strlen(name) + 1; + if (nameneed > PATH_MAX*2) + totbuff = EF_SAFE_ALLOC(nameneed); + strcpy(totbuff,pathbuff); + strcat(totbuff,name); + fd = open(totbuff, mode, FILE_MODE); + if (totbuff != sbuff) + EF_FREE(totbuff); + } else { + fd = open(name, mode, FILE_MODE); + } +#else + fd = open(name, mode, FILE_MODE); +#endif + +#ifdef VXWORKS + + /* This is a VxWorks/nfs workaround for erl_tar to create + * non-existant directories. (of some reason (...) VxWorks + * returns, the *non-module-prefixed*, 0xd code when + * trying to write a file in a directory that doesn't exist). + * (see efile_mkdir) + */ + if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) { + /* Return the correct error code enoent */ + errno = S_nfsLib_NFSERR_NOENT; + return check_error(-1, errInfo); + } +#endif + + if (!check_error(fd, errInfo)) + return 0; + + *pfd = fd; + if (pSize) { + *pSize = statbuf.st_size; + } + return 1; +} + +int +efile_may_openfile(Efile_error* errInfo, char *name) { + struct stat statbuf; /* Information about the file */ + int result; + + result = stat(name, &statbuf); + if (!check_error(result, errInfo)) + return 0; + if (!ISREG(statbuf)) { + errno = EISDIR; + return check_error(-1, errInfo); + } + return 1; +} + +void +efile_closefile(int fd) +{ + close(fd); +} + +int +efile_fsync(Efile_error *errInfo, /* Where to return error codes. */ + int fd) /* File descriptor for file to sync. */ +{ +#ifdef NO_FSYNC +#ifdef VXWORKS + return check_error(ioctl(fd, FIOSYNC, 0), errInfo); +#else + undefined fsync +#endif /* VXWORKS */ +#else +#if defined(DARWIN) && defined(F_FULLFSYNC) + return check_error(fcntl(fd, F_FULLFSYNC), errInfo); +#else + return check_error(fsync(fd), errInfo); +#endif /* DARWIN */ +#endif /* NO_FSYNC */ +} + +int +efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo, + char* name, int info_for_link) +{ + struct stat statbuf; /* Information about the file */ + struct tm *timep; /* Broken-apart filetime. */ + int result; + +#ifdef VXWORKS + if (*name == '\0') { + errInfo->posix_errno = errInfo->os_errno = ENOENT; + return 0; + } +#endif + + CHECK_PATHLEN(name, errInfo); + + if (info_for_link) { +#if (defined(VXWORKS) || defined(_OSE_)) + result = stat(name, &statbuf); +#else + result = lstat(name, &statbuf); +#endif + } else { + result = stat(name, &statbuf); + } + if (!check_error(result, errInfo)) { + return 0; + } + +#if SIZEOF_OFF_T == 4 + pInfo->size_high = 0; +#else + pInfo->size_high = (Uint32)(statbuf.st_size >> 32); +#endif + pInfo->size_low = (Uint32)statbuf.st_size; + +#ifdef NO_ACCESS + /* Just look at read/write access for owner. */ +#ifdef VXWORKS + + pInfo->access = FA_NONE; + if(statbuf.st_mode & S_IRUSR) + pInfo->access |= FA_READ; + if(statbuf.st_mode & S_IWUSR) + pInfo->access |= FA_WRITE; + +#else + + pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1; + +#endif /* VXWORKS */ +#else + pInfo->access = FA_NONE; + if (access(name, R_OK) == 0) + pInfo->access |= FA_READ; + if (access(name, W_OK) == 0) + pInfo->access |= FA_WRITE; + +#endif + + if (ISDEV(statbuf)) + pInfo->type = FT_DEVICE; + else if (ISDIR(statbuf)) + pInfo->type = FT_DIRECTORY; + else if (ISREG(statbuf)) + pInfo->type = FT_REGULAR; + else if (ISLNK(statbuf)) + pInfo->type = FT_SYMLINK; + else + pInfo->type = FT_OTHER; + +#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS) + { + /* Use the reentrant version of localtime() */ + static struct tm local_tm; +#define localtime(a) (localtime_r((a), &local_tm), &local_tm) +#endif + + +#define GET_TIME(dst, src) \ + timep = localtime(&statbuf.src); \ + (dst).year = timep->tm_year+1900; \ + (dst).month = timep->tm_mon+1; \ + (dst).day = timep->tm_mday; \ + (dst).hour = timep->tm_hour; \ + (dst).minute = timep->tm_min; \ + (dst).second = timep->tm_sec + + GET_TIME(pInfo->accessTime, st_atime); + GET_TIME(pInfo->modifyTime, st_mtime); + GET_TIME(pInfo->cTime, st_ctime); + +#undef GET_TIME + +#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS) + } +#endif + + pInfo->mode = statbuf.st_mode; + pInfo->links = statbuf.st_nlink; + pInfo->major_device = statbuf.st_dev; +#ifdef _OSE_ + pInfo->minor_device = 0; +#else + pInfo->minor_device = statbuf.st_rdev; +#endif + pInfo->inode = statbuf.st_ino; + pInfo->uid = statbuf.st_uid; + pInfo->gid = statbuf.st_gid; + + return 1; +} + +int +efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name) +{ + CHECK_PATHLEN(name, errInfo); + +#ifdef VXWORKS + + if (pInfo->mode != -1) { + int fd; + struct stat statbuf; + + fd = open(name, O_RDONLY, 0); + if (!check_error(fd, errInfo)) + return 0; + if (fstat(fd, &statbuf) < 0) { + close(fd); + return check_error(-1, errInfo); + } + if (pInfo->mode & S_IWUSR) { + /* clear read only bit */ + statbuf.st_attrib &= ~DOS_ATTR_RDONLY; + } else { + /* set read only bit */ + statbuf.st_attrib |= DOS_ATTR_RDONLY; + } + /* This should work for dos files but not for nfs ditos, so don't + * report errors (to avoid problems when running e.g. erl_tar) + */ + ioctl(fd, FIOATTRIBSET, statbuf.st_attrib); + close(fd); + } +#else + /* + * On some systems chown will always fail for a non-root user unless + * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as + * you don't try to chown a file to someone besides youself. + */ + +#ifndef _OSE_ + if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) { + return check_error(-1, errInfo); + } +#endif + + if (pInfo->mode != -1) { + mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID | + S_IRWXU | S_IRWXG | S_IRWXO); + if (chmod(name, newMode)) { + newMode &= ~(S_ISUID | S_ISGID); + if (chmod(name, newMode)) { + return check_error(-1, errInfo); + } + } + } + +#endif /* !VXWORKS */ + +#ifndef _OSE_ + + if (pInfo->accessTime.year != -1 && pInfo->modifyTime.year != -1) { + struct utimbuf tval; + struct tm timebuf; + +#define MKTIME(tb, ts) \ + timebuf.tm_year = ts.year-1900; \ + timebuf.tm_mon = ts.month-1; \ + timebuf.tm_mday = ts.day; \ + timebuf.tm_hour = ts.hour; \ + timebuf.tm_min = ts.minute; \ + timebuf.tm_sec = ts.second; \ + timebuf.tm_isdst = -1; \ + if ((tb = mktime(&timebuf)) == (time_t) -1) { \ + errno = EINVAL; \ + return check_error(-1, errInfo); \ + } + + MKTIME(tval.actime, pInfo->accessTime); + MKTIME(tval.modtime, pInfo->modifyTime); +#undef MKTIME + +#ifdef VXWORKS + /* VxWorks' utime doesn't work when the file is a nfs mounted + * one, don't report error if utime fails. + */ + utime(name, &tval); + return 1; +#else + return check_error(utime(name, &tval), errInfo); +#endif + } +#endif /* !_OSE_ */ + return 1; +} + + +int +efile_write(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was + opened. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count) /* Number of bytes to write. */ +{ + ssize_t written; /* Bytes written in last operation. */ + +#ifdef VXWORKS + if (flags & EFILE_MODE_APPEND) { + lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ + } +#endif + while (count > 0) { + if ((written = write(fd, buf, count)) < 0) { + if (errno != EINTR) + return check_error(-1, errInfo); + else + written = 0; + } + ASSERT(written <= count); + buf += written; + count -= written; + } + return 1; +} + +int +efile_writev(Efile_error* errInfo, /* Where to return error codes */ + int flags, /* Flags given when file was + * opened */ + int fd, /* File descriptor to write to */ + SysIOVec* iov, /* Vector of buffer structs. + * The structs are unchanged + * after the call */ + int iovcnt, /* Number of structs in vector */ + size_t size) /* Number of bytes to write */ +{ + int cnt = 0; /* Buffers so far written */ + int p = 0; /* Position in next buffer */ + + ASSERT(iovcnt >= 0); + +#ifdef VXWORKS + if (flags & EFILE_MODE_APPEND) { + lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */ + } +#endif + + while (cnt < iovcnt) { +#ifdef HAVE_WRITEV + int w; /* Bytes written in this call */ + int b = iovcnt - cnt; /* Buffers to write */ + if (b > MAXIOV) + b = MAXIOV; + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + if (b == 1) { + /* Degenerated io vector */ + do { + w = write(fd, iov[cnt].iov_base + p, iov[cnt].iov_len - p); + } while (w < 0 && errno == EINTR); + } else { + /* Non-empty vector first. + * Adjust pos in first buffer in case of + * previous incomplete writev */ + iov[cnt].iov_base += p; + iov[cnt].iov_len -= p; + do { + w = writev(fd, &iov[cnt], b); + } while (w < 0 && errno == EINTR); + iov[cnt].iov_base -= p; + iov[cnt].iov_len += p; + } + if (w < 0) + return check_error(-1, errInfo); + } else { + /* Empty vector first - skip */ + cnt++; + continue; + } + ASSERT(w >= 0); + /* Move forward to next vector to write */ + for (; cnt < iovcnt; cnt++) { + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + if (w < iov[cnt].iov_len) + break; + else + w -= iov[cnt].iov_len; + } + } + ASSERT(w >= 0); + p = w > 0 ? w : 0; /* Skip p bytes next writev */ +#else /* #ifdef HAVE_WRITEV */ + if (iov[cnt].iov_base && iov[cnt].iov_len > 0) { + /* Non-empty vector */ + int w; /* Bytes written in this call */ + while (p < iov[cnt].iov_len) { + do { + w = write(fd, iov[cnt].iov_base + p, iov[cnt].iov_len - p); + } while (w < 0 && errno == EINTR); + if (w < 0) + return check_error(-1, errInfo); + p += w; + } + } + cnt++; + p = 0; +#endif /* #ifdef HAVE_WRITEV */ + } /* while (cnt< iovcnt) */ + size = 0; /* Avoid compiler warning */ + return 1; +} + +int +efile_read(Efile_error* errInfo, /* Where to return error codes. */ + int flags, /* Flags given when file was opened. */ + int fd, /* File descriptor to read from. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return number of + bytes read. */ +{ + ssize_t n; + + for (;;) { + if ((n = read(fd, buf, count)) >= 0) + break; + else if (errno != EINTR) + return check_error(-1, errInfo); + } + *pBytesRead = (size_t) n; + return 1; +} + + +/* pread() and pwrite() */ +/* Some unix systems, notably Solaris has these syscalls */ +/* It is especially nice for i.e. the dets module to have support */ +/* for this, even if the underlying OS dosn't support it, it is */ +/* reasonably easy to work around by first calling seek, and then */ +/* calling read(). */ +/* This later strategy however changes the file pointer, which pread() */ +/* does not do. We choose to ignore this and say that the location */ +/* of the file pointer is undefined after a call to any of the p functions*/ + + +int +efile_pread(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to read from. */ + Sint64 offset, /* Offset in bytes from BOF. */ + char* buf, /* Buffer to read into. */ + size_t count, /* Number of bytes to read. */ + size_t *pBytesRead) /* Where to return + number of bytes read. */ +{ +#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) + ssize_t n; + off_t off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + for (;;) { + if ((n = pread(fd, buf, count, offset)) >= 0) + break; + else if (errno != EINTR) + return check_error(-1, errInfo); + } + *pBytesRead = (size_t) n; + return 1; +#else + { + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + if (res) { + return efile_read(errInfo, 0, fd, buf, count, pBytesRead); + } else { + return res; + } + } +#endif +} + + + +int +efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to write to. */ + char* buf, /* Buffer to write. */ + size_t count, /* Number of bytes to write. */ + Sint64 offset) /* where to write it */ +{ +#if defined(HAVE_PREAD) && defined(HAVE_PWRITE) + ssize_t written; /* Bytes written in last operation. */ + off_t off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + while (count > 0) { + if ((written = pwrite(fd, buf, count, offset)) < 0) { + if (errno != EINTR) + return check_error(-1, errInfo); + else + written = 0; + } + ASSERT(written <= count); + buf += written; + count -= written; + offset += written; + } + return 1; +#else /* For unix systems that don't support pread() and pwrite() */ + { + int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL); + + if (res) { + return efile_write(errInfo, 0, fd, buf, count); + } else { + return res; + } + } +#endif +} + + +int +efile_seek(Efile_error* errInfo, /* Where to return error codes. */ + int fd, /* File descriptor to do the seek on. */ + Sint64 offset, /* Offset in bytes from the given + origin. */ + int origin, /* Origin of seek (SEEK_SET, SEEK_CUR, + SEEK_END). */ + Sint64 *new_location) /* Resulting new location in file. */ +{ + off_t off, result; + + switch (origin) { + case EFILE_SEEK_SET: origin = SEEK_SET; break; + case EFILE_SEEK_CUR: origin = SEEK_CUR; break; + case EFILE_SEEK_END: origin = SEEK_END; break; + default: + errno = EINVAL; + return check_error(-1, errInfo); + } + off = (off_t) offset; + if (off != offset) { + errno = EINVAL; + return check_error(-1, errInfo); + } + + errno = 0; + result = lseek(fd, off, origin); + + /* + * Note that the man page for lseek (on SunOs 5) says: + * + * "if fildes is a remote file descriptor and offset is + * negative, lseek() returns the file pointer even if it is + * negative." + */ + + if (result < 0 && errno == 0) + errno = EINVAL; + if (result < 0) + return check_error(-1, errInfo); + if (new_location) { + *new_location = result; + } + return 1; +} + + +int +efile_truncate_file(Efile_error* errInfo, int *fd, int flags) +{ +#ifdef VXWORKS + off_t offset; + char namebuf[PATH_MAX+1]; + char namebuf2[PATH_MAX+10]; + int new; + int dummy; + int i; + int left; + static char buff[1024]; + struct stat st; + Efile_error tmperr; + + if ((offset = lseek(*fd, 0, 1)) < 0) { + return check_error((int) offset,errInfo); + } + if (ftruncate(*fd, offset) < 0) { + if (vxworks_to_posix(errno) != EINVAL) { + return check_error(-1, errInfo); + } + /* + ** Kludge + */ + if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) { + return check_error(-1, errInfo); + } + for(i=0;i<1000;++i) { + sprintf(namebuf2,"%s%d",namebuf,i); + CHECK_PATHLEN(namebuf2,errInfo); + if (stat(namebuf2,&st) < 0) { + break; + } + } + if (i > 1000) { + errno = EINVAL; + return check_error(-1, errInfo); + } + if (close(*fd) < 0) { + return check_error(-1, errInfo); + } + if (efile_rename(&tmperr,namebuf,namebuf2) < 0) { + i = check_error(-1,&tmperr); + if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, + fd,&dummy)) { + *fd = -1; + } else { + *errInfo = tmperr; + } + return i; + } + if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) { + i = check_error(-1,errInfo); + efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ + if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, + fd,&dummy)) { + *fd = -1; + } else { + lseek(*fd,offset,SEEK_SET); + } + return i; + } + /* Point of no return... */ + + if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) { + close(*fd); + *fd = -1; + return 0; + } + left = offset; + + while (left) { + if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) { + i = check_error(-1,errInfo); + close(new); + close(*fd); + unlink(namebuf); + efile_rename(&tmperr,namebuf2,namebuf); /* at least try */ + if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, + fd,&dummy)) { + *fd = -1; + } else { + lseek(*fd,offset,SEEK_SET); + } + return i; + } + left -= i; + if (write(new,buff,i) < 0) { + i = check_error(-1,errInfo); + close(new); + close(*fd); + unlink(namebuf); + rename(namebuf2,namebuf); /* at least try */ + if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE, + fd,&dummy)) { + *fd = -1; + } else { + lseek(*fd,offset,SEEK_SET); + } + return i; + } + } + close(*fd); + unlink(namebuf2); + close(new); + i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd, + &dummy); + if (i) { + lseek(*fd,offset,SEEK_SET); + } + return i; + } + return 1; +#else +#ifndef NO_FTRUNCATE + off_t offset; + + return check_error((offset = lseek(*fd, 0, 1)) >= 0 && + ftruncate(*fd, offset) == 0 ? 1 : -1, + errInfo); +#else + return 1; +#endif +#endif +} + +int +efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ +#ifdef _OSE_ + return ose_enotsup(errInfo); +#else +#ifdef VXWORKS + return vxworks_enotsup(errInfo); +#else + int len; + ASSERT(size > 0); + len = readlink(name, buffer, size-1); + if (len == -1) { + return check_error(-1, errInfo); + } + buffer[len] = '\0'; + return 1; +#endif +#endif +} + +int +efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size) +{ + errno = ENOTSUP; + return check_error(-1, errInfo); +} + +int +efile_link(Efile_error* errInfo, char* old, char* new) +{ +#ifdef _OSE_ + return ose_enotsup(errInfo); +#else +#ifdef VXWORKS + return vxworks_enotsup(errInfo); +#else + return check_error(link(old, new), errInfo); +#endif +#endif +} + +int +efile_symlink(Efile_error* errInfo, char* old, char* new) +{ +#ifdef _OSE_ + return ose_enotsup(errInfo); +#else +#ifdef VXWORKS + return vxworks_enotsup(errInfo); +#else + return check_error(symlink(old, new), errInfo); +#endif +#endif +} |