aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/drivers/unix/unix_efile.c
blob: e7dd9d2bdb06c6e79d9207c45a5995b99b2a844a (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   
  
                                                        
  




                                                                      
  



                                                                         
  


















                                                            
                                                                                         

                         
 










































                                                                          












                                                                        







                                                                         
                                     



























                                                                             
                                          

















                                                             
                                                         























































































































































































                                                                           











































































                                                                           





                                                                      

































































                                                                             
                                        










































































































                                                                            

                                                                        






























                                                                     
                                                 
                                     





















































































                                                                           


                                  



































































                                                                              










                                                                              






















                                                                         











                                                          
                      




















































                                                       


                                           
 

                                           
                                         
                                          


                                         






                                                                     

                        


































                                                                          


                                                                













                                                                     

                                     
 
              




                                                                 
     
                                                    
      





































                                                                              


                                                                       

                                                                 









                                                                            




                                                                         
                  




                                                               


                                                  






















                                                                           
             

                                     
                               




































































































































































































































































































                                                                              











                                         











                                                                          




                                                




                                                         




                                                   
 










                                                                           

                    


                                                                    

                                          





                                                                      






                                                                         




                                                                          

                                                           
                                                                           
 
                       
                      

                   
                                                         







                                                                      
                                                            
                      
                                                       




                                                                  
                       


                              
                                                         












                                                              
                     


               
                                                         











                                                               

                                                    














                                                                          


                                        
                          
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 1997-2011. 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
#if defined(HAVE_SENDFILE) && (defined(__linux__) || (defined(__sun) && defined(__SVR4)))
#include <sys/sendfile.h>
#endif

#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.
 */

#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

#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)

void erl_exit(int n, char *fmt, ...);

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 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 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 */

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);
    if (unlink(name) == 0) {
	return 1;
    }
    if (errno == EISDIR) {	/* Linux sets the wrong error code. */
	errno = EPERM;
    }
    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)
    /*
     * 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)		/* 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.
     */

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

    if (flags & EFILE_MODE_EXCL) {
	mode |= O_EXCL;
    }

#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_fdatasync(Efile_error *errInfo, /* Where to return error codes. */
	    int fd)               /* File descriptor for file to sync data. */
{
#ifdef HAVE_FDATASYNC
    return check_error(fdatasync(fd), errInfo);
#else
    return efile_fsync(errInfo, fd);
#endif
}

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

    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->minor_device = statbuf.st_rdev;
    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)
{
    struct utimbuf tval;

    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.
     */

    if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) {
	return check_error(-1, errInfo);
    }

    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 */

    tval.actime  = pInfo->accessTime;
    tval.modtime = pInfo->modifyTime;

#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
}


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 may be changed i.e.
				      * due to incomplete writes */
	     int iovcnt)             /* Number of structs in vector */
{
    int cnt = 0;                     /* Buffers so far written */

    ASSERT(iovcnt >= 0);
    
#ifdef VXWORKS
    if (flags & EFILE_MODE_APPEND) {
	lseek(fd, 0, SEEK_END);      /* Naive append emulation on VXWORKS */
    }
#endif

    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 */
#ifdef HAVE_WRITEV
	    int b = iovcnt - cnt;        /* Buffers to write */
	    /* Use as many buffers as MAXIOV allows */
	    if (b > MAXIOV)
		b = MAXIOV;
	    if (b > 1) {
		do {
		    w = writev(fd, &iov[cnt], b);
		} while (w < 0 && errno == EINTR);
	    } else
		/* Degenerated io vector - use regular write */
#endif
		{
		    do {
			w = write(fd, iov[cnt].iov_base, iov[cnt].iov_len);
		    } while (w < 0 && errno == EINTR);
		    ASSERT(w <= iov[cnt].iov_len);
		}
	    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;

    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 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
}

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 VXWORKS
    return vxworks_enotsup(errInfo);
#else
    return check_error(link(old, new), errInfo);
#endif
}

int
efile_symlink(Efile_error* errInfo, char* old, char* new)
{
#ifdef VXWORKS
    return vxworks_enotsup(errInfo);
#else
    return check_error(symlink(old, new), errInfo);
#endif
}

int
efile_fadvise(Efile_error* errInfo, int fd, Sint64 offset,
		  Sint64 length, int advise)
{
#ifdef HAVE_POSIX_FADVISE
    return check_error(posix_fadvise(fd, offset, length, advise), errInfo);
#else
    return check_error(0, errInfo);
#endif
}

#ifdef HAVE_SENDFILE

// For some reason the maximum size_t cannot be used as the max size
// 3GB seems to work on all platforms
#define SENDFILE_CHUNK_SIZE ((1 << 30) -1)

/*
 * sendfile: The implementation of the sendfile system call varies
 * a lot on different *nix platforms so to make the api similar in all
 * we have to emulate some things in linux and play with variables on
 * bsd/darwin.
 *
 * All of the calls will split a command which tries to send more than
 * SENDFILE_CHUNK_SIZE of data at once.
 *
 * On platforms where *nbytes of 0 does not mean the entire file, this is
 * simulated.
 *
 * It could be possible to implement header/trailer in sendfile. Though
 * you would have to emulate it in linux and on BSD/Darwin some complex
 * calculations have to be made when using a non blocking socket to figure
 * out how much of the header/file/trailer was sent in each command.
 */

int
efile_sendfile(Efile_error* errInfo, int in_fd, int out_fd,
	       off_t *offset, Uint64 *nbytes, struct t_sendfile_hdtl* hdtl)
{
    Uint64 written = 0;
#if defined(__linux__)
    ssize_t retval;
    do {
      // check if *nbytes is 0 or greater than chunk size
      if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
	retval = sendfile(out_fd, in_fd, offset, SENDFILE_CHUNK_SIZE);
      else
	retval = sendfile(out_fd, in_fd, offset, *nbytes);
      if (retval > 0) {
	written += retval;
	*nbytes -= retval;
      }
    } while (retval != -1 && retval == SENDFILE_CHUNK_SIZE);
    *nbytes = written;
    return check_error(retval == -1 ? -1 : 0, errInfo);
#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV)
    ssize_t retval;
    size_t len;
    sendfilevec_t fdrec;
    fdrec.sfv_fd = in_fd;
    fdrec.sfv_flag = 0;
    do {
      fdrec.sfv_off = *offset;
      len = 0;
      // check if *nbytes is 0 or greater than chunk size
      if (*nbytes == 0 || *nbytes > SENDFILE_CHUNK_SIZE)
	fdrec.sfv_len = SENDFILE_CHUNK_SIZE;
      else
	fdrec.sfv_len = *nbytes;
      retval = sendfilev(out_fd, &fdrec, 1, &len);
      if (retval != -1 || errno == EAGAIN || errno == EINTR) {
        *offset += len;
	*nbytes -= len;
	written += len;
      }
    } while (len == SENDFILE_CHUNK_SIZE);
    *nbytes = written;
    return check_error(retval == -1 ? -1 : 0, errInfo);
#elif defined(DARWIN)
    int retval;
    off_t len;
    do {
      // check if *nbytes is 0 or greater than chunk size
      if(*nbytes > SENDFILE_CHUNK_SIZE)
	len = SENDFILE_CHUNK_SIZE;
      else
	len = *nbytes;
      retval = sendfile(in_fd, out_fd, *offset, &len, NULL, 0);
      if (retval != -1 || errno == EAGAIN || errno == EINTR) {
        *offset += len;
	*nbytes -= len;
	written += len;
      }
    } while (len == SENDFILE_CHUNK_SIZE);
    *nbytes = written;
    return check_error(retval, errInfo);
#elif defined(__FreeBSD__) || defined(__DragonFly__)
    off_t len;
    int retval;
    do {
      if (*nbytes > SENDFILE_CHUNK_SIZE)
	retval = sendfile(in_fd, out_fd, *offset, SENDFILE_CHUNK_SIZE,
			  NULL, &len, 0);
      else
	retval = sendfile(in_fd, out_fd, *offset, *nbytes, NULL, &len, 0);
      if (retval != -1 || errno == EAGAIN || errno == EINTR) {
	*offset += len;
	*nbytes -= len;
	written += len;
      }
    } while(len == SENDFILE_CHUNK_SIZE);
    *nbytes = written;
    return check_error(retval, errInfo);
#endif
}
#endif /* HAVE_SENDFILE */