/* * %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 ./ * 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); }