aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/ose/ose_efile.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/drivers/ose/ose_efile.c')
-rw-r--r--erts/emulator/drivers/ose/ose_efile.c1124
1 files changed, 1124 insertions, 0 deletions
diff --git a/erts/emulator/drivers/ose/ose_efile.c b/erts/emulator/drivers/ose/ose_efile.c
new file mode 100644
index 0000000000..035ff81a9b
--- /dev/null
+++ b/erts/emulator/drivers/ose/ose_efile.c
@@ -0,0 +1,1124 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2012. 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 OSE.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#if defined(HAVE_POSIX_FALLOCATE) && !defined(__sun) && !defined(__sun__)
+#define _XOPEN_SOURCE 600
+#endif
+#if !defined(_GNU_SOURCE) && defined(HAVE_LINUX_FALLOC_H)
+#define _GNU_SOURCE
+#endif
+#include "sys.h"
+#include "erl_driver.h"
+#include "erl_efile.h"
+#if defined(DARWIN) || defined(HAVE_LINUX_FALLOC_H) || defined(HAVE_POSIX_FALLOCATE)
+#include "fcntl.h"
+#endif
+#include "ose.h"
+#include "unistd.h"
+#include "sys/stat.h"
+#include "dirent.h"
+#include "sys/time.h"
+#include "time.h"
+#include "assert.h"
+
+/* 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.
+ */
+
+#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
+
+#define IS_DOT_OR_DOTDOT(s) \
+ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
+
+/*
+ * Macros for handling local file descriptors
+ * and mutexes.
+ *
+ * Handling of files like this is necessary because OSE
+ * does not allow seeking after the end of a file. So
+ * what we do it emulate this by keeping track of the size
+ * of the file and where the file's positions is. If a
+ * write happens after eof then we pad it.
+ *
+ * Given time this should be rewritten to get rid of the
+ * mutex and use the port lock to protect the data. This
+ * could be done be done by adapting the efile api for some
+ * calls to allow some meta-data to be associated with the
+ * open file.
+ */
+
+#define L_FD_IS_VALID(fd_data) ((fd_data)->beyond_eof > 0)
+#define L_FD_INVALIDATE(fd_data) (fd_data)->beyond_eof = 0
+#define L_FD_CUR(fd_data) (fd_data)->pos
+#define L_FD_OFFS_BEYOND_EOF(fd_data, offs) \
+ (((fd_data)->size > offs) ? 0 : 1)
+
+#define L_FD_FAIL -1
+#define L_FD_SUCCESS 1
+#define L_FD_PAD_SIZE 255
+
+struct fd_meta {
+ ErlDrvMutex *meta_mtx;
+ struct fd_data *fd_data_list;
+};
+
+struct fd_data {
+ int fd;
+ struct fd_data *next;
+ struct fd_data *prev;
+ int pos;
+ int beyond_eof;
+ size_t size;
+#ifdef DEBUG
+ PROCESS owner;
+#endif
+};
+
+static int l_invalidate_local_fd(int fd);
+static int l_pad_file(struct fd_data *fd_data, off_t offset);
+static int check_error(int result, Efile_error* errInfo);
+static struct fd_data* l_new_fd(void);
+static int l_remove_local_fd(int fd);
+static struct fd_data* l_find_local_fd(int fd);
+static int l_update_local_fd(int fd, int pos, int size);
+
+static struct fd_meta* fdm = NULL;
+
+
+/***************************************************************************/
+
+static int
+l_remove_local_fd(int fd)
+{
+ struct fd_data *fd_data;
+ fd_data = l_find_local_fd(fd);
+
+ if (fd_data == NULL) {
+ return L_FD_FAIL;
+ }
+#ifdef DEBUG
+ assert(fd_data->owner == current_process());
+#endif
+ erl_drv_mutex_lock(fdm->meta_mtx);
+ /* head ? */
+ if (fd_data == fdm->fd_data_list) {
+ if (fd_data->next != NULL) {
+ /* remove link to head */
+ fd_data->next->prev = NULL;
+ /* set new head */
+ fdm->fd_data_list = fd_data->next;
+ }
+ else {
+ /* head is lonely */
+ fdm->fd_data_list = NULL;
+ }
+ }
+ else { /* not head */
+ if (fd_data->prev == NULL) {
+ erl_drv_mutex_unlock(fdm->meta_mtx);
+ return L_FD_FAIL;
+ }
+ else {
+ if (fd_data->next != NULL) {
+ fd_data->next->prev = fd_data->prev;
+ fd_data->prev->next = fd_data->next;
+ }
+ else {
+ fd_data->prev->next = NULL;
+ }
+ }
+ }
+
+ /* scramble values */
+ fd_data->beyond_eof = -1;
+ fd_data->next = NULL;
+ fd_data->prev = NULL;
+ fd_data->fd = -1;
+
+ /* unlock and clean */
+ driver_free(fd_data);
+ erl_drv_mutex_unlock(fdm->meta_mtx);
+
+ return L_FD_SUCCESS;
+}
+
+/***************************************************************************/
+
+static int
+l_invalidate_local_fd(int fd) {
+ struct fd_data *fd_data;
+
+ if ((fd_data = l_find_local_fd(fd)) == NULL) {
+ return L_FD_FAIL;
+ }
+
+ fd_data->beyond_eof = 0;
+ return L_FD_SUCCESS;
+}
+
+/****************************************************************************/
+
+static struct fd_data*
+l_find_local_fd(int fd) {
+ struct fd_data *fd_data;
+
+ fd_data = NULL;
+ erl_drv_mutex_lock(fdm->meta_mtx);
+ for (fd_data = fdm->fd_data_list; fd_data != NULL; ) {
+ if (fd_data->fd == fd) {
+#ifdef DEBUG
+ assert(fd_data->owner == current_process());
+#endif
+ break;
+ }
+ fd_data = fd_data->next;
+ }
+ erl_drv_mutex_unlock(fdm->meta_mtx);
+ return fd_data;
+}
+
+/***************************************************************************/
+
+static struct fd_data*
+l_new_fd(void) {
+ struct fd_data *fd_data;
+
+ fd_data = driver_alloc(sizeof(struct fd_data));
+ if (fd_data == NULL) {
+ return NULL;
+ }
+ erl_drv_mutex_lock(fdm->meta_mtx);
+ if (fdm->fd_data_list == NULL) {
+ fdm->fd_data_list = fd_data;
+ fdm->fd_data_list->prev = NULL;
+ fdm->fd_data_list->next = NULL;
+ }
+ else {
+ fd_data->next = fdm->fd_data_list;
+ fdm->fd_data_list = fd_data;
+ fdm->fd_data_list->prev = NULL;
+ }
+#ifdef DEBUG
+ fd_data->owner = current_process();
+#endif
+ erl_drv_mutex_unlock(fdm->meta_mtx);
+ return fd_data;
+}
+
+/***************************************************************************/
+
+static int
+l_update_local_fd(int fd, int pos, int size) {
+ struct fd_data *fd_data = NULL;
+
+ fd_data = l_find_local_fd(fd);
+ /* new fd to handle? */
+ if (fd_data == NULL) {
+ fd_data = l_new_fd();
+ if (fd_data == NULL) {
+ /* out of memory */
+ return L_FD_FAIL;
+ }
+ }
+ fd_data->size = size;
+ fd_data->pos = pos;
+ fd_data->fd = fd;
+ fd_data->beyond_eof = 1;
+
+ return L_FD_SUCCESS;
+}
+
+/***************************************************************************/
+
+static int
+l_pad_file(struct fd_data *fd_data, off_t offset) {
+ int size_dif;
+ int written = 0;
+ int ret_val = L_FD_SUCCESS;
+ char padding[L_FD_PAD_SIZE];
+
+ size_dif = (offset - fd_data->size);
+ memset(&padding, '\0', L_FD_PAD_SIZE);
+
+ while (size_dif > 0) {
+ written = write(fd_data->fd, padding,
+ (size_dif < L_FD_PAD_SIZE) ?
+ size_dif : L_FD_PAD_SIZE);
+ if (written < 0 && errno != EINTR && errno != EAGAIN) {
+ ret_val = -1;
+ break;
+ }
+ size_dif -= written;
+ }
+ L_FD_INVALIDATE(fd_data);
+ return ret_val;
+}
+
+/***************************************************************************/
+
+static int
+check_error(int result, Efile_error *errInfo) {
+ if (result < 0) {
+ errInfo->posix_errno = errInfo->os_errno = errno;
+ return 0;
+ }
+ return 1;
+}
+
+/***************************************************************************/
+
+int
+efile_init() {
+ fdm = driver_alloc(sizeof(struct fd_meta));
+ if (fdm == NULL) {
+ return L_FD_FAIL;
+ }
+ fdm->meta_mtx = erl_drv_mutex_create("ose_efile local fd mutex\n");
+ erl_drv_mutex_lock(fdm->meta_mtx);
+ fdm->fd_data_list = NULL;
+ erl_drv_mutex_unlock(fdm->meta_mtx);
+ return L_FD_SUCCESS;
+}
+
+/***************************************************************************/
+
+int
+efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to create. */
+{
+#ifdef NO_MKDIR_MODE
+ return check_error(mkdir(name), errInfo);
+#else
+ int res = mkdir(name, DIR_MODE);
+ if (res < 0 && errno == EINVAL) {
+ errno = ENOENT;
+ }
+ return check_error(res, errInfo);
+#endif
+}
+
+/***************************************************************************/
+
+int
+efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to delete. */
+{
+ if (rmdir(name) == 0) {
+ return 1;
+ }
+ if (errno == ENOTEMPTY) {
+ errno = EEXIST;
+ }
+ if (errno == EEXIST || errno == EINVAL) {
+ int saved_errno = errno;
+ struct stat file_stat;
+ struct stat cwd_stat;
+
+ if(stat(name, &file_stat) != 0) {
+ errno = ENOENT;
+ return check_error(-1, errInfo);
+ }
+ /*
+ * 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 = EACCES;
+ }
+ errno = saved_errno;
+ }
+ return check_error(-1, errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of file to delete. */
+{
+ struct stat statbuf;
+
+ if (stat(name, &statbuf) >= 0) {
+ /* Do not let unlink() remove directories */
+ if (ISDIR(statbuf)) {
+ errno = EPERM;
+ return check_error(-1, errInfo);
+ }
+
+ if (unlink(name) == 0) {
+ return 1;
+ }
+
+ if (errno == EISDIR) {
+ errno = EPERM;
+ return check_error(-1, errInfo);
+ }
+ }
+ else {
+ if (errno == EINVAL) {
+ errno = ENOENT;
+ return check_error(-1, errInfo);
+ }
+ }
+ 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. */
+{
+
+ /* temporary fix AFM does not recognize ./<file name>
+ * in destination remove pending on adaption of AFM fix
+ */
+
+ char *dot_str;
+ if (dst != NULL) {
+ dot_str = strchr(dst, '.');
+ if (dot_str && dot_str == dst && dot_str[1] == '/') {
+ dst = dst+2;
+ }
+ }
+
+ if (rename(src, dst) == 0) {
+ return 1;
+ }
+ if (errno == ENOTEMPTY) {
+ errno = EEXIST;
+ }
+ if (errno == EINVAL) {
+ struct stat file_stat;
+
+ if (stat(dst, &file_stat)== 0) {
+ if (ISDIR(file_stat)) {
+ errno = EISDIR;
+ }
+ else if (ISREG(file_stat)) {
+ errno = ENOTDIR;
+ }
+ else {
+ errno = EINVAL;
+ }
+ }
+ else {
+ errno = EINVAL;
+ }
+ }
+
+ if (strcmp(src, "/") == 0) {
+ errno = EINVAL;
+ }
+ return check_error(-1, errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to make current. */
+{
+ 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);
+
+ 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) /* in-out Size of buffer, length
+ of name. */
+{
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+
+ /*
+ * If this is the first call, we must open the directory.
+ */
+
+ 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);
+ *size = strlen(dirp->d_name);
+ 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. */
+
+ if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+
+ 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;
+ mode |= O_APPEND;
+ }
+
+ if (flags & EFILE_MODE_EXCL) {
+ mode |= O_EXCL;
+ }
+
+ fd = open(name, mode, FILE_MODE);
+
+ 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)
+{
+ if (l_find_local_fd(fd) != NULL) {
+ l_remove_local_fd(fd);
+ }
+ close(fd);
+}
+
+/***************************************************************************/
+
+int
+efile_fdatasync(Efile_error *errInfo, /* Where to return error codes. */
+ int fd) /* File descriptor for file to sync data. */
+{
+ return efile_fsync(errInfo, fd);
+}
+
+/***************************************************************************/
+
+int
+efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
+ int fd) /* File descriptor for file to sync. */
+{
+ return check_error(fsync(fd), errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
+ char* name, int info_for_link)
+{
+ struct stat statbuf; /* Information about the file */
+ int result;
+
+ 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. */
+
+ pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
+
+#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;
+
+ pInfo->accessTime = statbuf.st_atime;
+ pInfo->modifyTime = statbuf.st_mtime;
+ pInfo->cTime = statbuf.st_ctime;
+
+ pInfo->mode = statbuf.st_mode;
+ pInfo->links = statbuf.st_nlink;
+ pInfo->major_device = statbuf.st_dev;
+ 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)
+{
+ /*
+ * 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.
+ */
+ 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);
+ }
+ }
+ }
+
+ 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. */
+ struct fd_data *fd_data;
+
+ if ((fd_data = l_find_local_fd(fd)) != NULL) {
+ if (L_FD_IS_VALID(fd_data)) {
+ /* we are beyond eof and need to pad*/
+ if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) {
+ return check_error(-1, errInfo);
+ }
+ }
+ }
+
+ 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 may be changed i.e.
+ * due to incomplete writes */
+ int iovcnt) /* Number of structs in vector */
+{
+ struct fd_data *fd_data;
+ int cnt = 0; /* Buffers so far written */
+
+ ASSERT(iovcnt >= 0);
+ if ((fd_data = l_find_local_fd(fd)) != NULL) {
+ if (L_FD_IS_VALID(fd_data)) {
+ /* we are beyond eof and need to pad*/
+ if (l_pad_file(fd_data, L_FD_CUR(fd_data)) < 0) {
+ return check_error(-1, errInfo);
+ }
+ }
+ }
+ while (cnt < iovcnt) {
+ if ((! iov[cnt].iov_base) || (iov[cnt].iov_len <= 0)) {
+ /* Empty buffer - skip */
+ cnt++;
+ }
+ else { /* Non-empty buffer */
+ ssize_t w; /* Bytes written in this call */
+ do {
+ w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len);
+ } while (w < 0 && errno == EINTR);
+
+ ASSERT(w <= iov[cnt].iov_len || w == -1);
+
+ if (w < 0) {
+ return check_error(-1, errInfo);
+ }
+ /* Move forward to next buffer to write */
+ for (; cnt < iovcnt && w > 0; cnt++) {
+ if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
+ if (w < iov[cnt].iov_len) {
+ /* Adjust the buffer for next write */
+ iov[cnt].iov_len -= w;
+ iov[cnt].iov_base += w;
+ w = 0;
+ break;
+ }
+ else {
+ w -= iov[cnt].iov_len;
+ }
+ }
+ }
+ ASSERT(w == 0);
+ } /* else Non-empty buffer */
+ } /* while (cnt< iovcnt) */
+ 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;
+ struct fd_data *fd_data;
+
+ if ((fd_data = l_find_local_fd(fd)) != NULL) {
+ if (L_FD_IS_VALID(fd_data)) {
+ *pBytesRead = 0;
+ return 1;
+ }
+ }
+ for (;;) {
+ if ((n = read(fd, buf, count)) >= 0) {
+ break;
+ }
+ else if (errno != EINTR) {
+ return check_error(-1, errInfo);
+ }
+ }
+ if (fd_data != NULL && L_FD_IS_VALID(fd_data)) {
+ L_FD_INVALIDATE(fd_data);
+ }
+ *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. */
+{
+ 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;
+ }
+}
+
+
+/***************************************************************************/
+
+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 */
+{
+ int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+
+ if (res) {
+ return efile_write(errInfo, 0, fd, buf, count);
+ } else {
+ return res;
+ }
+}
+
+/***************************************************************************/
+
+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;
+ off = (off_t) offset;
+
+ 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);
+ }
+
+ if (off != offset) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+
+ errno = 0;
+ result = lseek(fd, off, origin);
+
+ if (result >= 0) {
+ l_invalidate_local_fd(fd);
+ }
+
+ if (result < 0)
+ {
+ if (errno == ENOSYS) {
+ int size, cur_pos;
+
+ if (off < 0) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+
+ cur_pos = lseek(fd, 0, SEEK_CUR);
+ size = lseek(fd, 0, SEEK_END);
+
+ if (origin == SEEK_SET) {
+ result = offset;
+ }
+ else if (origin == SEEK_CUR) {
+ result = offset + cur_pos;
+ }
+ else if (origin == SEEK_END) {
+ result = size + offset;
+ }
+
+ /* sanity check our result */
+ if (size > result) {
+ return check_error(-1, errInfo);
+ }
+
+ /* store the data localy */
+ l_update_local_fd(fd, result, size);
+
+ /* reset the original file position */
+ if (origin != SEEK_END) {
+ lseek(fd, cur_pos, SEEK_SET);
+ }
+ }
+ else if (errno == 0) {
+ errno = EINVAL;
+ }
+ }
+
+ if (new_location) {
+ *new_location = result;
+ }
+
+ return 1;
+}
+
+/***************************************************************************/
+
+int
+efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
+{
+ off_t offset;
+ struct fd_data *fd_data;
+
+ if ((fd_data = l_find_local_fd(*fd)) != NULL && L_FD_IS_VALID(fd_data)) {
+ offset = L_FD_CUR(fd_data);
+ }
+ else {
+ offset = lseek(*fd, 0, SEEK_CUR);
+ }
+
+ return check_error(((offset >= 0) &&
+ (ftruncate(*fd, offset) == 0)) ? 1 : -1, errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+/***************************************************************************/
+
+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)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_symlink(Efile_error* errInfo, char* old, char* new)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+/***************************************************************************/
+
+int
+efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
+ Sint64 length, int advise)
+{
+ return check_error(posix_fadvise(fd, offset, length, advise), errInfo);
+}
+
+/***************************************************************************/
+
+static int
+call_posix_fallocate(int fd, Sint64 offset, Sint64 length)
+{
+ int ret;
+
+ /*
+ * On Linux and Solaris for example, posix_fallocate() returns
+ * a positive error number on error and it does not set errno.
+ * On FreeBSD however (9.0 at least), it returns -1 on error
+ * and it sets errno.
+ */
+ do {
+ ret = posix_fallocate(fd, (off_t) offset, (off_t) length);
+ if (ret > 0) {
+ errno = ret;
+ ret = -1;
+ }
+ } while (ret != 0 && errno == EINTR);
+
+ return ret;
+}
+
+/***************************************************************************/
+
+int
+efile_fallocate(Efile_error* errInfo, int fd, Sint64 offset, Sint64 length)
+{
+ return check_error(call_posix_fallocate(fd, offset, length), errInfo);
+}