1636 lines
37 KiB
C
1636 lines
37 KiB
C
/*
|
|
Copyright (c) 2010-2020 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <hio-fio.h>
|
|
#include "hio-prv.h"
|
|
|
|
#if defined(_WIN32)
|
|
# include <windows.h>
|
|
/*# include <psapi.h>*/ /* for GetMappedFileName(). but dynamically loaded */
|
|
# include <tchar.h>
|
|
# if !defined(INVALID_SET_FILE_POINTER)
|
|
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
|
|
# endif
|
|
#elif defined(__OS2__)
|
|
# define INCL_DOSFILEMGR
|
|
# define INCL_DOSMODULEMGR
|
|
# define INCL_DOSPROCESS
|
|
# define INCL_DOSERRORS
|
|
# include <os2.h>
|
|
#elif defined(__DOS__)
|
|
# include <io.h>
|
|
# include <fcntl.h>
|
|
# include <errno.h>
|
|
#elif defined(vms) || defined(__vms)
|
|
# define __NEW_STARLET 1
|
|
# include <starlet.h>
|
|
# include <rms.h>
|
|
#else
|
|
# include <unistd.h>
|
|
# include <fcntl.h>
|
|
# include <errno.h>
|
|
#endif
|
|
|
|
/* internal status codes */
|
|
enum
|
|
{
|
|
STATUS_APPEND = (1 << 0),
|
|
STATUS_NOCLOSE = (1 << 1),
|
|
STATUS_WIN32_STDIN = (1 << 2)
|
|
};
|
|
|
|
#if defined(_WIN32)
|
|
|
|
typedef DWORD WINAPI (*getmappedfilename_t) (
|
|
HANDLE hProcess,
|
|
LPVOID lpv,
|
|
LPTSTR lpFilename,
|
|
DWORD nSize
|
|
);
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
#if defined(__WATCOMC__) && (__WATCOMC__ < 1200) && !defined(LONGLONG_INCLUDED)
|
|
typedef struct _LONGLONG {
|
|
ULONG ulLo;
|
|
LONG ulHi;
|
|
} LONGLONG, *PLONGLONG;
|
|
|
|
typedef struct _ULONGLONG {
|
|
ULONG ulLo;
|
|
ULONG ulHi;
|
|
} ULONGLONG, *PULONGLONG;
|
|
#endif
|
|
|
|
typedef APIRET APIENTRY (*dosopenl_t) (
|
|
PSZ pszFileName,
|
|
PHFILE pHf,
|
|
PULONG pulAction,
|
|
LONGLONG cbFile,
|
|
ULONG ulAttribute,
|
|
ULONG fsOpenFlags,
|
|
ULONG fsOpenMode,
|
|
PEAOP2 peaop2
|
|
);
|
|
|
|
typedef APIRET APIENTRY (*dossetfileptrl_t) (
|
|
HFILE hFile,
|
|
LONGLONG ib,
|
|
ULONG method,
|
|
PLONGLONG ibActual
|
|
);
|
|
|
|
typedef APIRET APIENTRY (*dossetfilesizel_t) (
|
|
HFILE hFile,
|
|
LONGLONG cbSize
|
|
);
|
|
|
|
static int dos_set = 0;
|
|
static dosopenl_t dos_open_l = HIO_NULL;
|
|
static dossetfileptrl_t dos_set_file_ptr_l = HIO_NULL;
|
|
static dossetfilesizel_t dos_set_file_size_l = HIO_NULL;
|
|
|
|
#endif
|
|
|
|
hio_fio_t* hio_fio_open (hio_gem_t* gem, hio_oow_t xtnsize, const hio_ooch_t* path, int flags, int mode)
|
|
{
|
|
hio_fio_t* fio;
|
|
|
|
fio = (hio_fio_t*)hio_gem_allocmem(gem, HIO_SIZEOF(hio_fio_t) + xtnsize);
|
|
if (fio)
|
|
{
|
|
if (hio_fio_init(fio, gem, path, flags, mode) <= -1)
|
|
{
|
|
hio_gem_freemem (gem, fio);
|
|
return HIO_NULL;
|
|
}
|
|
else HIO_MEMSET (fio + 1, 0, xtnsize);
|
|
}
|
|
return fio;
|
|
}
|
|
|
|
void hio_fio_close (hio_fio_t* fio)
|
|
{
|
|
hio_fio_fini (fio);
|
|
hio_gem_freemem (fio->gem, fio);
|
|
}
|
|
|
|
int hio_fio_init (hio_fio_t* fio, hio_gem_t* gem, const hio_ooch_t* path, int flags, int mode)
|
|
{
|
|
hio_fio_hnd_t handle;
|
|
|
|
hio_uint32_t temp_no;
|
|
hio_ooch_t* temp_ptr;
|
|
hio_bch_t* temp_ptr_b;
|
|
hio_oow_t temp_tries;
|
|
|
|
#if defined(_WIN32)
|
|
int fellback = 0;
|
|
#endif
|
|
|
|
#if defined(__OS2__)
|
|
if (!dos_set)
|
|
{
|
|
DosEnterCritSec ();
|
|
if (!dos_set)
|
|
{
|
|
HMODULE mod;
|
|
if (DosLoadModule(NULL, 0, "DOSCALL1", &mod) == NO_ERROR)
|
|
{
|
|
/* look up routines by ordinal */
|
|
DosQueryProcAddr (mod, 981, NULL, (PFN*)&dos_open_l);
|
|
DosQueryProcAddr (mod, 988, NULL, (PFN*)&dos_set_file_ptr_l);
|
|
DosQueryProcAddr (mod, 989, NULL, (PFN*)&dos_set_file_size_l);
|
|
}
|
|
|
|
dos_set = 1;
|
|
}
|
|
DosExitCritSec ();
|
|
}
|
|
#endif
|
|
|
|
HIO_MEMSET (fio, 0, HIO_SIZEOF(*fio));
|
|
fio->gem = gem;
|
|
|
|
if (!(flags & (HIO_FIO_READ | HIO_FIO_WRITE | HIO_FIO_APPEND | HIO_FIO_HANDLE)))
|
|
{
|
|
/* one of HIO_FIO_READ, HIO_FIO_WRITE, HIO_FIO_APPEND,
|
|
* and HIO_FIO_HANDLE must be specified */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
/* Store some flags for later use */
|
|
if (flags & HIO_FIO_NOCLOSE)
|
|
fio->status |= STATUS_NOCLOSE;
|
|
|
|
if (flags & HIO_FIO_TEMPORARY)
|
|
{
|
|
hio_ntime_t now;
|
|
|
|
/*if (flags & (HIO_FIO_HANDLE | HIO_FIO_BCSTRPATH))*/
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
/* HIO_FIO_TEMPORARY and HIO_FIO_HANDLE/HIO_FIO_BCSTRPATH
|
|
* are mutually exclusive */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
temp_no = 0;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
|
|
for (temp_ptr_b = (hio_bch_t*)path; *temp_ptr_b; temp_ptr_b++)
|
|
temp_no += *temp_ptr_b;
|
|
|
|
if (temp_ptr_b - (hio_bch_t*)path < 4)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
temp_ptr_b -= 4;
|
|
}
|
|
else
|
|
{
|
|
/* if HIO_FIO_TEMPORARY is used, the path name must be writable. */
|
|
for (temp_ptr = (hio_ooch_t*)path; *temp_ptr; temp_ptr++)
|
|
temp_no += *temp_ptr;
|
|
|
|
/* The path name template must be at least 4 characters long
|
|
* excluding the terminating null. this function fails if not */
|
|
if (temp_ptr - path < 4)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
temp_ptr -= 4;
|
|
}
|
|
|
|
hio_get_ntime (&now);
|
|
temp_no += (now.sec & 0xFFFFFFFFlu);
|
|
|
|
temp_tries = 0;
|
|
|
|
retry_temporary:
|
|
temp_tries++;
|
|
|
|
/* Fails after 5000 tries. 5000 randomly chosen */
|
|
if (temp_tries > 5000)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
/* Generate the next random number to use to make a
|
|
* new path name */
|
|
temp_no = hio_rand31(temp_no);
|
|
|
|
/*
|
|
* You must not pass a constant string for a path name
|
|
* when HIO_FIO_TEMPORARY is set, because it changes
|
|
* the path name with a random number generated
|
|
*/
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
hio_fmt_uintmax_to_bcstr (
|
|
temp_ptr_b,
|
|
4,
|
|
temp_no % 0x10000,
|
|
16 | HIO_FMT_UINTMAX_NOTRUNC | HIO_FMT_UINTMAX_NONULL,
|
|
4,
|
|
'\0',
|
|
HIO_NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
hio_fmt_uintmax_to_oocstr (
|
|
temp_ptr,
|
|
4,
|
|
temp_no % 0x10000,
|
|
16 | HIO_FMT_UINTMAX_NOTRUNC | HIO_FMT_UINTMAX_NONULL,
|
|
4,
|
|
HIO_T('\0'),
|
|
HIO_NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
handle = *(hio_fio_hnd_t*)path;
|
|
/* do not specify an invalid handle value */
|
|
/*HIO_ASSERT (hio, handle != INVALID_HANDLE_VALUE);*/
|
|
|
|
if (handle == GetStdHandle (STD_INPUT_HANDLE))
|
|
fio->status |= STATUS_WIN32_STDIN;
|
|
}
|
|
else
|
|
{
|
|
DWORD desired_access = 0;
|
|
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
|
DWORD creation_disposition = 0;
|
|
DWORD flag_and_attr = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
if (fellback) share_mode &= ~FILE_SHARE_DELETE;
|
|
|
|
if (flags & HIO_FIO_APPEND)
|
|
{
|
|
if (fellback)
|
|
{
|
|
desired_access |= GENERIC_WRITE;
|
|
}
|
|
else
|
|
{
|
|
/* this is not officially documented for CreateFile.
|
|
* ZwCreateFile (kernel) seems to document it */
|
|
fio->status &= ~STATUS_APPEND;
|
|
desired_access |= FILE_APPEND_DATA;
|
|
}
|
|
}
|
|
else if (flags & HIO_FIO_WRITE)
|
|
{
|
|
/* In WIN32, FILE_APPEND_DATA and GENERIC_WRITE can't
|
|
* be used together */
|
|
desired_access |= GENERIC_WRITE;
|
|
}
|
|
if (flags & HIO_FIO_READ) desired_access |= GENERIC_READ;
|
|
|
|
if (flags & HIO_FIO_CREATE)
|
|
{
|
|
creation_disposition =
|
|
(flags & HIO_FIO_EXCLUSIVE)? CREATE_NEW:
|
|
(flags & HIO_FIO_TRUNCATE)? CREATE_ALWAYS: OPEN_ALWAYS;
|
|
}
|
|
else if (flags & HIO_FIO_TRUNCATE)
|
|
{
|
|
creation_disposition = TRUNCATE_EXISTING;
|
|
}
|
|
else creation_disposition = OPEN_EXISTING;
|
|
|
|
if (flags & HIO_FIO_NOSHREAD)
|
|
share_mode &= ~FILE_SHARE_READ;
|
|
if (flags & HIO_FIO_NOSHWRITE)
|
|
share_mode &= ~FILE_SHARE_WRITE;
|
|
if (flags & HIO_FIO_NOSHDELETE)
|
|
share_mode &= ~FILE_SHARE_DELETE;
|
|
|
|
if (!(mode & HIO_FIO_WUSR))
|
|
flag_and_attr = FILE_ATTRIBUTE_READONLY;
|
|
if (flags & HIO_FIO_SYNC)
|
|
flag_and_attr |= FILE_FLAG_WRITE_THROUGH;
|
|
|
|
#if defined(FILE_FLAG_OPEN_REPARSE_POINT)
|
|
if (flags & HIO_FIO_NOFOLLOW)
|
|
flag_and_attr |= FILE_FLAG_OPEN_REPARSE_POINT;
|
|
#endif
|
|
|
|
/* these two are just hints to OS */
|
|
if (flags & HIO_FIO_RANDOM)
|
|
flag_and_attr |= FILE_FLAG_RANDOM_ACCESS;
|
|
if (flags & HIO_FIO_SEQUENTIAL)
|
|
flag_and_attr |= FILE_FLAG_SEQUENTIAL_SCAN;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
handle = CreateFileA(
|
|
(const hio_bch_t*)path, desired_access, share_mode,
|
|
HIO_NULL, /* set noinherit by setting no secattr */
|
|
creation_disposition, flag_and_attr, 0
|
|
);
|
|
}
|
|
else
|
|
{
|
|
handle = CreateFile(
|
|
path, desired_access, share_mode,
|
|
HIO_NULL, /* set noinherit by setting no secattr */
|
|
creation_disposition, flag_and_attr, 0
|
|
);
|
|
}
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
DWORD e = GetLastError();
|
|
if (!fellback && e == ERROR_INVALID_PARAMETER &&
|
|
((share_mode & FILE_SHARE_DELETE) || (flags & HIO_FIO_APPEND)))
|
|
{
|
|
/* old windows fails with ERROR_INVALID_PARAMETER
|
|
* when some advanced flags are used. so try again
|
|
* with fallback flags */
|
|
fellback = 1;
|
|
|
|
share_mode &= ~FILE_SHARE_DELETE;
|
|
if (flags & HIO_FIO_APPEND)
|
|
{
|
|
fio->status |= STATUS_APPEND;
|
|
desired_access &= ~FILE_APPEND_DATA;
|
|
desired_access |= GENERIC_WRITE;
|
|
}
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
handle = CreateFileA(
|
|
(const hio_bch_t*)path, desired_access, share_mode,
|
|
HIO_NULL, /* set noinherit by setting no secattr */
|
|
creation_disposition, flag_and_attr, 0
|
|
);
|
|
}
|
|
else
|
|
{
|
|
handle = CreateFile(
|
|
path, desired_access, share_mode,
|
|
HIO_NULL, /* set noinherit by setting no secattr */
|
|
creation_disposition, flag_and_attr, 0
|
|
);
|
|
}
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
i if (flags & HIO_FIO_TEMPORARY) goto retry_temporary;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (flags & HIO_FIO_TEMPORARY) goto retry_temporary;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(e));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* some special check */
|
|
#if 0
|
|
if (GetFileType(handle) == FILE_TYPE_UNKNOWN)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
CloseHandle (handle);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* TODO: support more features on WIN32 - TEMPORARY, DELETE_ON_CLOSE */
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
handle = *(hio_fio_hnd_t*)path;
|
|
}
|
|
else
|
|
{
|
|
APIRET ret;
|
|
ULONG action_taken = 0;
|
|
ULONG open_action, open_mode, open_attr;
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
const hio_bch_t* path_mb = path;
|
|
#else
|
|
hio_bch_t path_mb_buf[CCHMAXPATH];
|
|
hio_bch_t* path_mb;
|
|
hio_oow_t wl, ml;
|
|
int px;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
path_mb = (hio_bch_t*)path;
|
|
}
|
|
else
|
|
{
|
|
path_mb = path_mb_buf;
|
|
ml = HIO_COUNTOF(path_mb_buf);
|
|
px = hio_gem_convutobcstr(fio->gem, path, &wl, path_mb, &ml);
|
|
if (px == -2)
|
|
{
|
|
/* the static buffer is too small.
|
|
* dynamically allocate a buffer */
|
|
path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NUL);
|
|
if (path_mb == HIO_NULL) return -1;
|
|
}
|
|
else if (px <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (flags & HIO_FIO_APPEND) fio->status |= STATUS_APPEND;
|
|
|
|
if (flags & HIO_FIO_CREATE)
|
|
{
|
|
if (flags & HIO_FIO_EXCLUSIVE)
|
|
{
|
|
open_action = OPEN_ACTION_FAIL_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
|
|
}
|
|
else if (flags & HIO_FIO_TRUNCATE)
|
|
{
|
|
open_action = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW;
|
|
}
|
|
else
|
|
{
|
|
open_action = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
|
|
}
|
|
}
|
|
else if (flags & HIO_FIO_TRUNCATE)
|
|
{
|
|
open_action = OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
|
|
}
|
|
else
|
|
{
|
|
open_action = OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW;
|
|
}
|
|
|
|
open_mode = OPEN_FLAGS_NOINHERIT;
|
|
|
|
if (flags & HIO_FIO_SYNC) open_mode |= OPEN_FLAGS_WRITE_THROUGH;
|
|
|
|
if ((flags & HIO_FIO_NOSHREAD) && (flags & HIO_FIO_NOSHWRITE)) open_mode |= OPEN_SHARE_DENYREADWRITE;
|
|
else if (flags & HIO_FIO_NOSHREAD) open_mode |= OPEN_SHARE_DENYREAD;
|
|
else if (flags & HIO_FIO_NOSHWRITE) open_mode |= OPEN_SHARE_DENYWRITE;
|
|
else open_mode |= OPEN_SHARE_DENYNONE;
|
|
|
|
if ((flags & HIO_FIO_READ) && (flags & HIO_FIO_WRITE)) open_mode |= OPEN_ACCESS_READWRITE;
|
|
else if (flags & HIO_FIO_READ) open_mode |= OPEN_ACCESS_READONLY;
|
|
else if (flags & HIO_FIO_WRITE) open_mode |= OPEN_ACCESS_WRITEONLY;
|
|
|
|
open_attr = (mode & HIO_FIO_WUSR)? FILE_NORMAL: FILE_READONLY;
|
|
|
|
#if defined(FIL_STANDARDL)
|
|
if (dos_open_l)
|
|
{
|
|
LONGLONG zero;
|
|
|
|
zero.ulLo = 0;
|
|
zero.ulHi = 0;
|
|
ret = dos_open_l (
|
|
path_mb, /* file name */
|
|
&handle, /* file handle */
|
|
&action_taken, /* store action taken */
|
|
zero, /* size */
|
|
open_attr, /* attribute */
|
|
open_action, /* action if it exists */
|
|
open_mode, /* open mode */
|
|
0L
|
|
);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ret = DosOpen (
|
|
path_mb, /* file name */
|
|
&handle, /* file handle */
|
|
&action_taken, /* store action taken */
|
|
0, /* size */
|
|
open_attr, /* attribute */
|
|
open_action, /* action if it exists */
|
|
open_mode, /* open mode */
|
|
0L
|
|
);
|
|
#if defined(FIL_STANDARDL)
|
|
}
|
|
#endif
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
|
|
if (ret != NO_ERROR)
|
|
{
|
|
if (flags & HIO_FIO_TEMPORARY) goto retry_temporary;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
handle = *(hio_fio_hnd_t*)path;
|
|
/* do not specify an invalid handle value */
|
|
/*HIO_ASSERT (hio, handle >= 0);*/
|
|
}
|
|
else
|
|
{
|
|
int oflags = 0;
|
|
int permission = 0;
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
const hio_bch_t* path_mb = path;
|
|
#else
|
|
hio_bch_t path_mb_buf[_MAX_PATH];
|
|
hio_bch_t* path_mb;
|
|
hio_oow_t wl, ml;
|
|
int px;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
path_mb = (hio_bch_t*)path;
|
|
}
|
|
else
|
|
{
|
|
path_mb = path_mb_buf;
|
|
ml = HIO_COUNTOF(path_mb_buf);
|
|
px = hio_gem_convutobcstr(fio->gem, path, &wl, path_mb, &ml);
|
|
if (px == -2)
|
|
{
|
|
/* static buffer size not enough.
|
|
* switch to dynamic allocation */
|
|
path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NULL);
|
|
if (path_mb == HIO_NULL) return -1;
|
|
}
|
|
else if (px <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (flags & HIO_FIO_APPEND)
|
|
{
|
|
if ((flags & HIO_FIO_READ)) oflags |= O_RDWR;
|
|
else oflags |= O_WRONLY;
|
|
oflags |= O_APPEND;
|
|
}
|
|
else
|
|
{
|
|
if ((flags & HIO_FIO_READ) &&
|
|
(flags & HIO_FIO_WRITE)) oflags |= O_RDWR;
|
|
else if (flags & HIO_FIO_READ) oflags |= O_RDONLY;
|
|
else if (flags & HIO_FIO_WRITE) oflags |= O_WRONLY;
|
|
}
|
|
|
|
if (flags & HIO_FIO_CREATE) oflags |= O_CREAT;
|
|
if (flags & HIO_FIO_TRUNCATE) oflags |= O_TRUNC;
|
|
if (flags & HIO_FIO_EXCLUSIVE) oflags |= O_EXCL;
|
|
|
|
oflags |= O_BINARY | O_NOINHERIT;
|
|
|
|
if (mode & HIO_FIO_RUSR) permission |= S_IREAD;
|
|
if (mode & HIO_FIO_WUSR) permission |= S_IWRITE;
|
|
|
|
handle = open (
|
|
path_mb,
|
|
oflags,
|
|
permission
|
|
);
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
|
|
if (handle <= -1)
|
|
{
|
|
if (flags & HIO_FIO_TEMPORARY) goto retry_temporary;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
/* TODO: implement this */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
struct FAB* fab;
|
|
struct RAB* rab;
|
|
unsigned long r0;
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
const hio_bch_t* path_mb = path;
|
|
#else
|
|
hio_bch_t path_mb_buf[1024];
|
|
hio_bch_t* path_mb;
|
|
hio_oow_t wl, ml;
|
|
int px;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
path_mb = (hio_bch_t*)path;
|
|
}
|
|
else
|
|
{
|
|
path_mb = path_mb_buf;
|
|
ml = HIO_COUNTOF(path_mb_buf);
|
|
px = hio_convutobcstr(fio->gem, path, &wl, path_mb, &ml);
|
|
if (px == -2)
|
|
{
|
|
/* the static buffer is too small.
|
|
* allocate a buffer */
|
|
path_mb = hio_duputobcstr(fio->gem, path, mmgr);
|
|
if (path_mb == HIO_NULL) return -1;
|
|
}
|
|
else if (px <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
rab = (struct RAB*)hio_gem_allocmem(fio->gem, HIO_SIZEOF(*rab) + HIO_SIZEOF(*fab));
|
|
if (rab == HIO_NULL)
|
|
{
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
fab = (struct FAB*)(rab + 1);
|
|
*rab = cc$rms_rab;
|
|
rab->rab$l_fab = fab;
|
|
|
|
*fab = cc$rms_fab;
|
|
fab->fab$l_fna = path_mb;
|
|
fab->fab$b_fns = strlen(path_mb);
|
|
fab->fab$b_org = FAB$C_SEQ;
|
|
fab->fab$b_rfm = FAB$C_VAR; /* FAB$C_STM, FAB$C_STMLF, FAB$C_VAR, etc... */
|
|
fab->fab$b_fac = FAB$M_GET | FAB$M_PUT;
|
|
|
|
fab->fab$b_fac = FAB$M_NIL;
|
|
if (flags & HIO_FIO_READ) fab->fab$b_fac |= FAB$M_GET;
|
|
if (flags & (HIO_FIO_WRITE | HIO_FIO_APPEND)) fab->fab$b_fac |= FAB$M_PUT | FAB$M_TRN; /* put, truncate */
|
|
|
|
fab->fab$b_shr |= FAB$M_SHRPUT | FAB$M_SHRGET; /* FAB$M_NIL */
|
|
if (flags & HIO_FIO_NOSHREAD) fab->fab$b_shr &= ~FAB$M_SHRGET;
|
|
if (flags & HIO_FIO_NOSHWRITE) fab->fab$b_shr &= ~FAB$M_SHRPUT;
|
|
|
|
if (flags & HIO_FIO_APPEND) rab->rab$l_rop |= RAB$M_EOF;
|
|
|
|
if (flags & HIO_FIO_CREATE)
|
|
{
|
|
if (flags & HIO_FIO_EXCLUSIVE)
|
|
fab->fab$l_fop &= ~FAB$M_CIF;
|
|
else
|
|
fab->fab$l_fop |= FAB$M_CIF;
|
|
|
|
r0 = sys$create (&fab, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
r0 = sys$open (&fab, 0, 0);
|
|
}
|
|
|
|
if (r0 != RMS$_NORMAL && r0 != RMS$_CREATED)
|
|
{
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0));
|
|
return -1;
|
|
}
|
|
|
|
|
|
r0 = sys$connect (&rab, 0, 0);
|
|
if (r0 != RMS$_NORMAL)
|
|
{
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0));
|
|
return -1;
|
|
}
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf) hio_gem_freemem (fio->gem, path_mb);
|
|
#endif
|
|
|
|
handle = rab;
|
|
}
|
|
|
|
#else
|
|
|
|
if (flags & HIO_FIO_HANDLE)
|
|
{
|
|
handle = *(hio_fio_hnd_t*)path;
|
|
/* do not specify an invalid handle value */
|
|
/*HIO_ASSERT (hio, handle >= 0);*/
|
|
}
|
|
else
|
|
{
|
|
int desired_access = 0;
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
const hio_bch_t* path_mb = path;
|
|
#else
|
|
hio_bch_t path_mb_buf[1024]; /* PATH_MAX instead? */
|
|
hio_bch_t* path_mb;
|
|
hio_oow_t wl, ml;
|
|
int px;
|
|
|
|
if (flags & HIO_FIO_BCSTRPATH)
|
|
{
|
|
path_mb = (hio_bch_t*)path;
|
|
}
|
|
else
|
|
{
|
|
path_mb = path_mb_buf;
|
|
ml = HIO_COUNTOF(path_mb_buf);
|
|
px = hio_conv_ucstr_to_bcstr_with_cmgr(path, &wl, path_mb, &ml, fio->gem->cmgr);
|
|
if (px == -2)
|
|
{
|
|
/* the static buffer is too small.
|
|
* allocate a buffer */
|
|
path_mb = hio_gem_duputobcstr(fio->gem, path, HIO_NULL);
|
|
if (path_mb == HIO_NULL) return -1;
|
|
}
|
|
else if (px <= -1)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
* rwa -> RDWR | APPEND
|
|
* ra -> RDWR | APPEND
|
|
* wa -> WRONLY | APPEND
|
|
* a -> WRONLY | APPEND
|
|
*/
|
|
if (flags & HIO_FIO_APPEND)
|
|
{
|
|
if ((flags & HIO_FIO_READ)) desired_access |= O_RDWR;
|
|
else desired_access |= O_WRONLY;
|
|
desired_access |= O_APPEND;
|
|
}
|
|
else
|
|
{
|
|
if ((flags & HIO_FIO_READ) &&
|
|
(flags & HIO_FIO_WRITE)) desired_access |= O_RDWR;
|
|
else if (flags & HIO_FIO_READ) desired_access |= O_RDONLY;
|
|
else if (flags & HIO_FIO_WRITE) desired_access |= O_WRONLY;
|
|
}
|
|
|
|
if (flags & HIO_FIO_CREATE) desired_access |= O_CREAT;
|
|
if (flags & HIO_FIO_TRUNCATE) desired_access |= O_TRUNC;
|
|
if (flags & HIO_FIO_EXCLUSIVE) desired_access |= O_EXCL;
|
|
#if defined(O_SYNC)
|
|
if (flags & HIO_FIO_SYNC) desired_access |= O_SYNC;
|
|
#endif
|
|
|
|
#if defined(O_NOFOLLOW)
|
|
if (flags & HIO_FIO_NOFOLLOW) desired_access |= O_NOFOLLOW;
|
|
#endif
|
|
|
|
#if defined(O_LARGEFILE)
|
|
desired_access |= O_LARGEFILE;
|
|
#endif
|
|
#if defined(O_CLOEXEC)
|
|
desired_access |= O_CLOEXEC; /* no inherit */
|
|
#endif
|
|
|
|
handle = HIO_OPEN(path_mb, desired_access, mode);
|
|
|
|
#if defined(HIO_OOCH_IS_BCH)
|
|
/* nothing to do */
|
|
#else
|
|
if (path_mb != path_mb_buf && path_mb != (hio_bch_t*)path)
|
|
{
|
|
hio_gem_freemem (fio->gem, path_mb);
|
|
}
|
|
#endif
|
|
if (handle == -1)
|
|
{
|
|
if (flags & HIO_FIO_TEMPORARY) goto retry_temporary;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
#if !defined(O_CLOEXEC) && defined(FD_CLOEXEC)
|
|
int flag = fcntl(handle, F_GETFD);
|
|
if (flag >= 0) fcntl (handle, F_SETFD, flag | FD_CLOEXEC);
|
|
#endif
|
|
}
|
|
|
|
/* set some file access hints */
|
|
#if defined(POSIX_FADV_RANDOM)
|
|
if (flags & HIO_FIO_RANDOM)
|
|
posix_fadvise (handle, 0, 0, POSIX_FADV_RANDOM);
|
|
#endif
|
|
#if defined(POSIX_FADV_SEQUENTIAL)
|
|
if (flags & HIO_FIO_SEQUENTIAL)
|
|
posix_fadvise (handle, 0, 0, POSIX_FADV_SEQUENTIAL);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
fio->handle = handle;
|
|
return 0;
|
|
}
|
|
|
|
void hio_fio_fini (hio_fio_t* fio)
|
|
{
|
|
if (!(fio->status & STATUS_NOCLOSE))
|
|
{
|
|
#if defined(_WIN32)
|
|
CloseHandle (fio->handle);
|
|
|
|
#elif defined(__OS2__)
|
|
DosClose (fio->handle);
|
|
|
|
#elif defined(__DOS__)
|
|
close (fio->handle);
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
struct RAB* rab = (struct RAB*)fio->handle;
|
|
sys$disconnect (rab, 0, 0);
|
|
sys$close ((struct FAB*)(rab + 1), 0, 0);
|
|
hio_gem_freemem (fio->gem, fio->handle);
|
|
#else
|
|
close (fio->handle);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
hio_fio_hnd_t hio_fio_gethnd (const hio_fio_t* fio)
|
|
{
|
|
return fio->handle;
|
|
}
|
|
|
|
hio_fio_off_t hio_fio_seek (hio_fio_t* fio, hio_fio_off_t offset, hio_fio_ori_t origin)
|
|
{
|
|
#if defined(_WIN32)
|
|
static int seek_map[] =
|
|
{
|
|
FILE_BEGIN,
|
|
FILE_CURRENT,
|
|
FILE_END
|
|
};
|
|
LARGE_INTEGER x;
|
|
#if defined(_WIN64)
|
|
LARGE_INTEGER y;
|
|
#endif
|
|
|
|
/* HIO_ASSERT (fio->hio, HIO_SIZEOF(offset) <= HIO_SIZEOF(x.QuadPart));*/
|
|
|
|
#if defined(_WIN64)
|
|
x.QuadPart = offset;
|
|
if (SetFilePointerEx (fio->handle, x, &y, seek_map[origin]) == FALSE)
|
|
{
|
|
return (hio_fio_off_t)-1;
|
|
}
|
|
return (hio_fio_off_t)y.QuadPart;
|
|
#else
|
|
|
|
/* SetFilePointerEx is not available on Windows NT 4.
|
|
* So let's use SetFilePointer */
|
|
x.QuadPart = offset;
|
|
x.LowPart = SetFilePointer (
|
|
fio->handle, x.LowPart, &x.HighPart, seek_map[origin]);
|
|
if (x.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return (hio_fio_off_t)-1;
|
|
}
|
|
return (hio_fio_off_t)x.QuadPart;
|
|
#endif
|
|
|
|
#elif defined(__OS2__)
|
|
static int seek_map[] =
|
|
{
|
|
FILE_BEGIN,
|
|
FILE_CURRENT,
|
|
FILE_END
|
|
};
|
|
|
|
#if defined(FIL_STANDARDL)
|
|
if (dos_set_file_ptr_l)
|
|
{
|
|
LONGLONG pos, newpos;
|
|
APIRET ret;
|
|
|
|
/*HIO_ASSERT (fio->hio, HIO_SIZEOF(offset) >= HIO_SIZEOF(pos));*/
|
|
|
|
pos.ulLo = (ULONG)(offset&0xFFFFFFFFlu);
|
|
pos.ulHi = (ULONG)(offset>>32);
|
|
|
|
ret = dos_set_file_ptr_l (fio->handle, pos, seek_map[origin], &newpos);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return (hio_fio_off_t)-1;
|
|
}
|
|
|
|
return ((hio_fio_off_t)newpos.ulHi << 32) | newpos.ulLo;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ULONG newpos;
|
|
APIRET ret;
|
|
|
|
ret = DosSetFilePtr (fio->handle, offset, seek_map[origin], &newpos);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return (hio_fio_off_t)-1;
|
|
}
|
|
|
|
return newpos;
|
|
#if defined(FIL_STANDARDL)
|
|
}
|
|
#endif
|
|
|
|
#elif defined(__DOS__)
|
|
static int seek_map[] =
|
|
{
|
|
SEEK_SET,
|
|
SEEK_CUR,
|
|
SEEK_END
|
|
};
|
|
|
|
return lseek (fio->handle, offset, seek_map[origin]);
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
/* TODO: */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return (hio_fio_off_t)-1;
|
|
#else
|
|
static int seek_map[] =
|
|
{
|
|
SEEK_SET,
|
|
SEEK_CUR,
|
|
SEEK_END
|
|
};
|
|
|
|
#if defined(HIO_LLSEEK)
|
|
loff_t tmp;
|
|
|
|
if (HIO_LLSEEK(fio->handle,
|
|
(unsigned long)(offset>>32),
|
|
(unsigned long)(offset&0xFFFFFFFFlu),
|
|
&tmp,
|
|
seek_map[origin]) == -1)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return (hio_fio_off_t)-1;
|
|
}
|
|
|
|
return (hio_fio_off_t)tmp;
|
|
#else
|
|
return lseek(fio->handle, offset, seek_map[origin]);
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
int hio_fio_truncate (hio_fio_t* fio, hio_fio_off_t size)
|
|
{
|
|
#if defined(_WIN32)
|
|
if (hio_fio_seek (fio, size, HIO_FIO_BEGIN) == (hio_fio_off_t)-1) return -1;
|
|
if (SetEndOfFile(fio->handle) == FALSE)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
#elif defined(__OS2__)
|
|
|
|
APIRET ret;
|
|
|
|
#if defined(FIL_STANDARDL)
|
|
if (dos_set_file_size_l)
|
|
{
|
|
LONGLONG sz;
|
|
/* the file must have the write access for it to succeed */
|
|
|
|
sz.ulLo = (ULONG)(size&0xFFFFFFFFlu);
|
|
sz.ulHi = (ULONG)(size>>32);
|
|
|
|
ret = dos_set_file_size_l (fio->handle, sz);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ret = DosSetFileSize (fio->handle, size);
|
|
#if defined(FIL_STANDARDL)
|
|
}
|
|
#endif
|
|
|
|
if (ret != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
int n;
|
|
n = chsize (fio->handle, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
unsigned long r0;
|
|
struct RAB* rab = (struct RAB*)fio->handle;
|
|
|
|
if ((r0 = sys$rewind (rab, 0, 0)) != RMS$_NORMAL ||
|
|
(r0 = sys$truncate (rab, 0, 0)) != RMS$_NORMAL)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
int n;
|
|
n = ftruncate(fio->handle, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#endif
|
|
}
|
|
|
|
hio_ooi_t hio_fio_read (hio_fio_t* fio, void* buf, hio_oow_t size)
|
|
{
|
|
#if defined(_WIN32)
|
|
|
|
DWORD count;
|
|
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD);
|
|
if (ReadFile (fio->handle, buf, (DWORD)size, &count, HIO_NULL) == FALSE)
|
|
{
|
|
DWORD e = GetLastError();
|
|
/* special case when ReadFile returns failure with ERROR_BROKEN_PIPE.
|
|
* this happens when an anonymous pipe is a standard input for redirection.
|
|
* assuming that ERROR_BROKEN_PIPE doesn't occur with normal
|
|
* input streams, i treat the condition as a normal EOF indicator. */
|
|
if ((fio->status & STATUS_WIN32_STDIN) && e == ERROR_BROKEN_PIPE) return 0;
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(e));
|
|
return -1;
|
|
}
|
|
return (hio_ooi_t)count;
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
APIRET ret;
|
|
ULONG count;
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG);
|
|
ret = DosRead (fio->handle, buf, (ULONG)size, &count);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return -1;
|
|
}
|
|
return (hio_ooi_t)count;
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
int n;
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int);
|
|
n = read (fio->handle, buf, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
unsigned long r0;
|
|
struct RAB* rab = (struct RAB*)fio->handle;
|
|
|
|
if (size > 32767) size = 32767;
|
|
|
|
rab->rab$l_ubf = buf;
|
|
rab->rab$w_usz = size;
|
|
|
|
r0 = sys$get (rab, 0, 0);
|
|
if (r0 != RMS$_NORMAL)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0));
|
|
return -1;
|
|
}
|
|
|
|
return rab->rab$w_rsz;
|
|
#else
|
|
|
|
hio_ooi_t n;
|
|
if (size > HIO_TYPE_MAX(hio_ooi_t)) size = HIO_TYPE_MAX(hio_ooi_t);
|
|
n = read(fio->handle, buf, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
hio_ooi_t hio_fio_write (hio_fio_t* fio, const void* data, hio_oow_t size)
|
|
{
|
|
#if defined(_WIN32)
|
|
|
|
DWORD count;
|
|
|
|
if (fio->status & STATUS_APPEND)
|
|
{
|
|
/* TODO: only when FILE_APPEND_DATA failed??? how do i know this??? */
|
|
/* i do this on a best-effort basis */
|
|
#if defined(_WIN64)
|
|
LARGE_INTEGER x;
|
|
x.QuadPart = 0;
|
|
SetFilePointerEx (fio->handle, x, HIO_NULL, FILE_END);
|
|
#else
|
|
SetFilePointer (fio->handle, 0, HIO_NULL, FILE_END);
|
|
#endif
|
|
}
|
|
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(DWORD);
|
|
if (WriteFile(fio->handle, data, (DWORD)size, &count, HIO_NULL) == FALSE)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
return (hio_ooi_t)count;
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
APIRET ret;
|
|
ULONG count;
|
|
|
|
if (fio->status & STATUS_APPEND)
|
|
{
|
|
/* i do this on a best-effort basis */
|
|
#if defined(FIL_STANDARDL)
|
|
if (dos_set_file_ptr_l)
|
|
{
|
|
LONGLONG pos, newpos;
|
|
pos.ulLo = (ULONG)0;
|
|
pos.ulHi = (ULONG)0;
|
|
dos_set_file_ptr_l (fio->handle, pos, FILE_END, &newpos);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
ULONG newpos;
|
|
DosSetFilePtr (fio->handle, 0, FILE_END, &newpos);
|
|
#if defined(FIL_STANDARDL)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(ULONG);
|
|
ret = DosWrite(fio->handle, (PVOID)data, (ULONG)size, &count);
|
|
if (ret != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(ret));
|
|
return -1;
|
|
}
|
|
return (hio_ooi_t)count;
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
int n;
|
|
if (size > (HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int)))
|
|
size = HIO_TYPE_MAX(hio_ooi_t) & HIO_TYPE_MAX(unsigned int);
|
|
n = write(fio->handle, data, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
unsigned long r0;
|
|
struct RAB* rab = (struct RAB*)fio->handle;
|
|
|
|
if (size > 32767) size = 32767;
|
|
|
|
rab->rab$l_rbf = (char*)data;
|
|
rab->rab$w_rsz = size;
|
|
|
|
r0 = sys$put(rab, 0, 0);
|
|
if (r0 != RMS$_NORMAL)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(r0));
|
|
return -1;
|
|
}
|
|
|
|
return rab->rab$w_rsz;
|
|
|
|
#else
|
|
|
|
hio_ooi_t n;
|
|
if (size > HIO_TYPE_MAX(hio_ooi_t)) size = HIO_TYPE_MAX(hio_ooi_t);
|
|
n = write(fio->handle, data, size);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
|
|
static int get_devname_from_handle (
|
|
hio_fio_t* fio, hio_ooch_t* buf, hio_oow_t len)
|
|
{
|
|
HANDLE map = NULL;
|
|
void* mem = NULL;
|
|
DWORD olen;
|
|
HINSTANCE psapi;
|
|
getmappedfilename_t getmappedfilename;
|
|
|
|
/* try to load psapi.dll dynamially for
|
|
* systems without it. direct linking to the library
|
|
* may end up with dependency failure on such systems.
|
|
* this way, the worst case is that this function simply
|
|
* fails. */
|
|
psapi = LoadLibrary (HIO_T("PSAPI.DLL"));
|
|
if (!psapi)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
|
|
getmappedfilename = (getmappedfilename_t)
|
|
GetProcAddress (psapi, HIO_BT("GetMappedFileName"));
|
|
if (!getmappedfilename)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
FreeLibrary (psapi);
|
|
return -1;
|
|
}
|
|
|
|
/* create a file mapping object */
|
|
map = CreateFileMapping (
|
|
fio->handle,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
1,
|
|
NULL
|
|
);
|
|
if (map == NULL)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
FreeLibrary (psapi);
|
|
return -1;
|
|
}
|
|
|
|
/* create a file mapping to get the file name. */
|
|
mem = MapViewOfFile (map, FILE_MAP_READ, 0, 0, 1);
|
|
if (mem == NULL)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
CloseHandle (map);
|
|
FreeLibrary (psapi);
|
|
return -1;
|
|
}
|
|
|
|
olen = getmappedfilename (GetCurrentProcess(), mem, buf, len);
|
|
if (olen == 0)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
UnmapViewOfFile (mem);
|
|
CloseHandle (map);
|
|
FreeLibrary (psapi);
|
|
return -1;
|
|
}
|
|
|
|
UnmapViewOfFile (mem);
|
|
CloseHandle (map);
|
|
FreeLibrary (psapi);
|
|
return 0;
|
|
}
|
|
|
|
static int get_volname_from_handle (hio_fio_t* fio, hio_ooch_t* buf, hio_oow_t len)
|
|
{
|
|
if (get_devname_from_handle (fio, buf, len) == -1) return -1;
|
|
|
|
if (hio_comp_oocstr_limited(buf, HIO_T("\\Device\\LanmanRedirector\\"), 25, 1) == 0)
|
|
{
|
|
/*buf[0] = HIO_T('\\');*/
|
|
hio_copy_oocstr_unlimited (&buf[1], &buf[24]);
|
|
}
|
|
else
|
|
{
|
|
DWORD n;
|
|
hio_ooch_t drives[128];
|
|
|
|
n = GetLogicalDriveStrings(HIO_COUNTOF(drives), drives);
|
|
|
|
if (n == 0 /* error */ ||
|
|
n > HIO_COUNTOF(drives) /* buffer small */)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
|
|
while (n > 0)
|
|
{
|
|
hio_ooch_t drv[3];
|
|
hio_ooch_t path[MAX_PATH];
|
|
|
|
drv[0] = drives[--n];
|
|
drv[1] = HIO_T(':');
|
|
drv[2] = HIO_T('\0');
|
|
if (QueryDosDevice (drv, path, HIO_COUNTOF(path)))
|
|
{
|
|
hio_oow_t pl = hio_count_oocstr(path);
|
|
hio_oow_t bl = hio_count_oocstr(buf);
|
|
if (bl > pl && buf[pl] == HIO_T('\\') &&
|
|
hio_comp_oochars(buf, pl, path, pl, 1) == 0)
|
|
{
|
|
buf[0] = drv[0];
|
|
buf[1] = HIO_T(':');
|
|
hio_copy_oocstr_unlimited (&buf[2], &buf[pl]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if the match is not found, the device name is returned
|
|
* without translation */
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int hio_fio_chmod (hio_fio_t* fio, int mode)
|
|
{
|
|
#if defined(_WIN32)
|
|
|
|
int flags = FILE_ATTRIBUTE_NORMAL;
|
|
hio_ooch_t name[MAX_PATH];
|
|
|
|
/* it is a best effort implementation. if the file size is 0,
|
|
* it can't even get the file name from the handle and thus fails.
|
|
* if GENERIC_READ is not set in CreateFile, CreateFileMapping fails.
|
|
* so if this fio is opened without HIO_FIO_READ, this function fails.
|
|
*/
|
|
if (get_volname_from_handle(fio, name, HIO_COUNTOF(name)) == -1) return -1;
|
|
|
|
if (!(mode & HIO_FIO_WUSR)) flags = FILE_ATTRIBUTE_READONLY;
|
|
if (SetFileAttributes(name, flags) == FALSE)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
APIRET n;
|
|
int flags = FILE_NORMAL;
|
|
#if defined(FIL_STANDARDL)
|
|
FILESTATUS3L stat;
|
|
#else
|
|
FILESTATUS3 stat;
|
|
#endif
|
|
ULONG size = HIO_SIZEOF(stat);
|
|
|
|
#if defined(FIL_STANDARDL)
|
|
n = DosQueryFileInfo(fio->handle, FIL_STANDARDL, &stat, size);
|
|
#else
|
|
n = DosQueryFileInfo(fio->handle, FIL_STANDARD, &stat, size);
|
|
#endif
|
|
if (n != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n));
|
|
return -1;
|
|
}
|
|
|
|
if (!(mode & HIO_FIO_WUSR)) flags = FILE_READONLY;
|
|
|
|
stat.attrFile = flags;
|
|
#if defined(FIL_STANDARDL)
|
|
n = DosSetFileInfo(fio->handle, FIL_STANDARDL, &stat, size);
|
|
#else
|
|
n = DosSetFileInfo(fio->handle, FIL_STANDARD, &stat, size);
|
|
#endif
|
|
if (n != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
int permission = 0;
|
|
|
|
if (mode & HIO_FIO_RUSR) permission |= S_IREAD;
|
|
if (mode & HIO_FIO_WUSR) permission |= S_IWRITE;
|
|
|
|
/* TODO: fchmod not available. find a way to do this
|
|
return fchmod (fio->handle, permission); */
|
|
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
/* TODO: */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return (hio_fio_off_t)-1;
|
|
|
|
#elif defined(HAVE_FCHMOD)
|
|
int n;
|
|
n = fchmod(fio->handle, mode);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#else
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
|
|
#endif
|
|
}
|
|
|
|
int hio_fio_sync (hio_fio_t* fio)
|
|
{
|
|
#if defined(_WIN32)
|
|
|
|
if (FlushFileBuffers(fio->handle) == FALSE)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(GetLastError()));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
APIRET n;
|
|
n = DosResetBuffer(fio->handle);
|
|
if (n != NO_ERROR)
|
|
{
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(n));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
#elif defined(__DOS__)
|
|
|
|
int n;
|
|
n = fsync(fio->handle);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
|
|
#elif defined(vms) || defined(__vms)
|
|
|
|
/* TODO: */
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return (hio_fio_off_t)-1;
|
|
|
|
#elif defined(HAVE_FSYNC)
|
|
|
|
int n;
|
|
n = fsync(fio->handle);
|
|
if (n <= -1) hio_gem_seterrnum (fio->gem, HIO_NULL, hio_syserr_to_errnum(errno));
|
|
return n;
|
|
#else
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int hio_fio_lock (hio_fio_t* fio, hio_fio_lck_t* lck, int flags)
|
|
{
|
|
/* TODO: hio_fio_lock
|
|
* struct flock fl;
|
|
* fl.l_type = F_RDLCK, F_WRLCK;
|
|
* HIO_FCNTL (fio->handle, F_SETLK, &fl);
|
|
*/
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
}
|
|
|
|
int hio_fio_unlock (hio_fio_t* fio, hio_fio_lck_t* lck, int flags)
|
|
{
|
|
/* TODO: hio_fio_unlock
|
|
* struct flock fl;
|
|
* fl.l_type = F_UNLCK;
|
|
* HIO_FCNTL (fio->handle, F_SETLK, &fl);
|
|
*/
|
|
hio_gem_seterrnum (fio->gem, HIO_NULL, HIO_ENOIMPL);
|
|
return -1;
|
|
}
|
|
|
|
int hio_get_std_fio_handle (hio_fio_std_t std, hio_fio_hnd_t* hnd)
|
|
{
|
|
#if defined(_WIN32)
|
|
static DWORD tab[] =
|
|
{
|
|
STD_INPUT_HANDLE,
|
|
STD_OUTPUT_HANDLE,
|
|
STD_ERROR_HANDLE
|
|
};
|
|
#elif defined(vms) || defined(__vms)
|
|
/* TODO */
|
|
static int tab[] = { 0, 1, 2 };
|
|
#else
|
|
|
|
static hio_fio_hnd_t tab[] =
|
|
{
|
|
#if defined(__OS2__)
|
|
(HFILE)0, (HFILE)1, (HFILE)2
|
|
#elif defined(__DOS__)
|
|
0, 1, 2
|
|
#else
|
|
0, 1, 2
|
|
#endif
|
|
};
|
|
|
|
#endif
|
|
|
|
if (std < 0 || std >= HIO_COUNTOF(tab)) return -1;
|
|
|
|
#if defined(_WIN32)
|
|
{
|
|
HANDLE tmp = GetStdHandle(tab[std]);
|
|
if (tmp == INVALID_HANDLE_VALUE) return -1;
|
|
*hnd = tmp;
|
|
}
|
|
#elif defined(vms) || defined(__vms)
|
|
/* TODO: */
|
|
return -1;
|
|
#else
|
|
*hnd = tab[std];
|
|
#endif
|
|
return 0;
|
|
}
|