feat:replace boost library with C++11 std library

This commit is contained in:
Livox-SDK
2020-04-01 20:53:55 +08:00
parent 559ffbf712
commit 47389056be
1936 changed files with 241862 additions and 60153 deletions
+65
View File
@@ -0,0 +1,65 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
#include "apr_thread_mutex.h"
APR_DECLARE(apr_status_t) apr_file_buffer_set(apr_file_t *file,
char * buffer,
apr_size_t bufsize)
{
apr_status_t rv;
if (file->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(file->mutex);
}
if(file->buffered) {
/* Flush the existing buffer */
rv = apr_file_flush(file);
if (rv != APR_SUCCESS) {
if (file->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(file->mutex);
}
return rv;
}
}
file->buffer = buffer;
file->bufsize = bufsize;
file->buffered = 1;
file->bufpos = 0;
file->direction = 0;
file->dataRead = 0;
if (file->bufsize == 0) {
/* Setting the buffer size to zero is equivalent to turning
* buffering off.
*/
file->buffered = 0;
}
if (file->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(file->mutex);
}
return APR_SUCCESS;
}
APR_DECLARE(apr_size_t) apr_file_buffer_size_get(apr_file_t *file)
{
return file->bufsize;
}
+408
View File
@@ -0,0 +1,408 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr.h"
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_strings.h"
#include "apr_portable.h"
#include "apr_arch_atime.h"
#if APR_HAVE_ERRNO_H
#include <errno.h>
#endif
#if APR_HAVE_STRING_H
#include <string.h>
#endif
#if APR_HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
static apr_status_t dir_cleanup(void *thedir)
{
apr_dir_t *dir = thedir;
if (dir->dirhand != INVALID_HANDLE_VALUE && !FindClose(dir->dirhand)) {
return apr_get_os_error();
}
dir->dirhand = INVALID_HANDLE_VALUE;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new, const char *dirname,
apr_pool_t *pool)
{
apr_status_t rv;
apr_size_t len = strlen(dirname);
(*new) = apr_pcalloc(pool, sizeof(apr_dir_t));
/* Leave room here to add and pop the '*' wildcard for FindFirstFile
* and double-null terminate so we have one character to change.
*/
(*new)->dirname = apr_palloc(pool, len + 3);
memcpy((*new)->dirname, dirname, len);
if (len && (*new)->dirname[len - 1] != '/') {
(*new)->dirname[len++] = '/';
}
(*new)->dirname[len++] = '\0';
(*new)->dirname[len] = '\0';
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
/* Create a buffer for the longest file name we will ever see
*/
(*new)->w.entry = apr_pcalloc(pool, sizeof(WIN32_FIND_DATAW));
(*new)->name = apr_pcalloc(pool, APR_FILE_MAX * 3 + 1);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
/* Note that we won't open a directory that is greater than MAX_PATH,
* counting the additional '/' '*' wildcard suffix. If a * won't fit
* then neither will any other file name within the directory.
* The length not including the trailing '*' is stored as rootlen, to
* skip over all paths which are too long.
*/
if (len >= APR_PATH_MAX) {
(*new) = NULL;
return APR_ENAMETOOLONG;
}
(*new)->n.entry = apr_pcalloc(pool, sizeof(WIN32_FIND_DATAW));
}
#endif
(*new)->rootlen = len - 1;
(*new)->pool = pool;
(*new)->dirhand = INVALID_HANDLE_VALUE;
apr_pool_cleanup_register((*new)->pool, (void *)(*new), dir_cleanup,
apr_pool_cleanup_null);
rv = apr_dir_read(NULL, 0, *new);
if (rv != APR_SUCCESS) {
dir_cleanup(*new);
*new = NULL;
}
return rv;
}
APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *dir)
{
apr_pool_cleanup_kill(dir->pool, dir, dir_cleanup);
return dir_cleanup(dir);
}
APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
apr_dir_t *thedir)
{
apr_status_t rv;
char *fname;
/* The while loops below allow us to skip all invalid file names, so that
* we aren't reporting any files where their absolute paths are too long.
*/
#if APR_HAS_UNICODE_FS
apr_wchar_t wdirname[APR_PATH_MAX];
apr_wchar_t *eos = NULL;
IF_WIN_OS_IS_UNICODE
{
/* This code path is always be invoked by apr_dir_open or
* apr_dir_rewind, so return without filling out the finfo.
*/
if (thedir->dirhand == INVALID_HANDLE_VALUE)
{
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wdirname, sizeof(wdirname)
/ sizeof(apr_wchar_t),
thedir->dirname))) {
return rv;
}
eos = wcschr(wdirname, '\0');
eos[0] = '*';
eos[1] = '\0';
thedir->dirhand = FindFirstFileW(wdirname, thedir->w.entry);
eos[0] = '\0';
if (thedir->dirhand == INVALID_HANDLE_VALUE) {
return apr_get_os_error();
}
thedir->bof = 1;
return APR_SUCCESS;
}
else if (thedir->bof) {
/* Noop - we already called FindFirstFileW from
* either apr_dir_open or apr_dir_rewind ... use
* that first record.
*/
thedir->bof = 0;
}
else if (!FindNextFileW(thedir->dirhand, thedir->w.entry)) {
return apr_get_os_error();
}
while (thedir->rootlen &&
thedir->rootlen + wcslen(thedir->w.entry->cFileName) >= APR_PATH_MAX)
{
if (!FindNextFileW(thedir->dirhand, thedir->w.entry)) {
return apr_get_os_error();
}
}
if ((rv = unicode_to_utf8_path(thedir->name, APR_FILE_MAX * 3 + 1,
thedir->w.entry->cFileName)))
return rv;
fname = thedir->name;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
/* This code path is always be invoked by apr_dir_open or
* apr_dir_rewind, so return without filling out the finfo.
*/
if (thedir->dirhand == INVALID_HANDLE_VALUE) {
/* '/' terminated, so add the '*' and pop it when we finish */
char *eop = strchr(thedir->dirname, '\0');
eop[0] = '*';
eop[1] = '\0';
thedir->dirhand = FindFirstFileA(thedir->dirname,
thedir->n.entry);
eop[0] = '\0';
if (thedir->dirhand == INVALID_HANDLE_VALUE) {
return apr_get_os_error();
}
thedir->bof = 1;
return APR_SUCCESS;
}
else if (thedir->bof) {
/* Noop - we already called FindFirstFileW from
* either apr_dir_open or apr_dir_rewind ... use
* that first record.
*/
thedir->bof = 0;
}
else if (!FindNextFileA(thedir->dirhand, thedir->n.entry)) {
return apr_get_os_error();
}
while (thedir->rootlen &&
thedir->rootlen + strlen(thedir->n.entry->cFileName) >= MAX_PATH)
{
if (!FindNextFileA(thedir->dirhand, thedir->n.entry)) {
return apr_get_os_error();
}
}
fname = thedir->n.entry->cFileName;
}
#endif
fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) thedir->w.entry,
0, wanted);
finfo->pool = thedir->pool;
finfo->valid |= APR_FINFO_NAME;
finfo->name = fname;
if (wanted &= ~finfo->valid) {
/* Go back and get more_info if we can't answer the whole inquiry
*/
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
/* Almost all our work is done. Tack on the wide file name
* to the end of the wdirname (already / delimited)
*/
if (!eos)
eos = wcschr(wdirname, '\0');
wcscpy(eos, thedir->w.entry->cFileName);
rv = more_finfo(finfo, wdirname, wanted, MORE_OF_WFSPEC);
eos[0] = '\0';
return rv;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
#if APR_HAS_UNICODE_FS
/* Don't waste stack space on a second buffer, the one we set
* aside for the wide directory name is twice what we need.
*/
char *fspec = (char*)wdirname;
#else
char fspec[APR_PATH_MAX];
#endif
apr_size_t dirlen = strlen(thedir->dirname);
if (dirlen >= sizeof(fspec))
dirlen = sizeof(fspec) - 1;
apr_cpystrn(fspec, thedir->dirname, sizeof(fspec));
apr_cpystrn(fspec + dirlen, fname, sizeof(fspec) - dirlen);
return more_finfo(finfo, fspec, wanted, MORE_OF_FSPEC);
}
#endif
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_dir_rewind(apr_dir_t *dir)
{
apr_status_t rv;
/* this will mark the handle as invalid and we'll open it
* again if apr_dir_read() is subsequently called
*/
rv = dir_cleanup(dir);
if (rv == APR_SUCCESS)
rv = apr_dir_read(NULL, 0, dir);
return rv;
}
APR_DECLARE(apr_status_t) apr_dir_make(const char *path, apr_fileperms_t perm,
apr_pool_t *pool)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wpath,
sizeof(wpath) / sizeof(apr_wchar_t),
path))) {
return rv;
}
if (!CreateDirectoryW(wpath, NULL)) {
return apr_get_os_error();
}
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
if (!CreateDirectory(path, NULL)) {
return apr_get_os_error();
}
#endif
return APR_SUCCESS;
}
static apr_status_t dir_make_parent(char *path,
apr_fileperms_t perm,
apr_pool_t *pool)
{
apr_status_t rv;
char *ch = strrchr(path, '\\');
if (!ch) {
return APR_ENOENT;
}
*ch = '\0';
rv = apr_dir_make (path, perm, pool); /* Try to make straight off */
if (APR_STATUS_IS_ENOENT(rv)) { /* Missing an intermediate dir */
rv = dir_make_parent(path, perm, pool);
if (rv == APR_SUCCESS || APR_STATUS_IS_EEXIST(rv)) {
rv = apr_dir_make(path, perm, pool); /* And complete the path */
}
}
*ch = '\\'; /* Always replace the slash before returning */
return rv;
}
APR_DECLARE(apr_status_t) apr_dir_make_recursive(const char *path,
apr_fileperms_t perm,
apr_pool_t *pool)
{
apr_status_t rv = 0;
rv = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
if (APR_STATUS_IS_ENOENT(rv)) { /* Missing an intermediate dir */
char *dir;
rv = apr_filepath_merge(&dir, "", path, APR_FILEPATH_NATIVE, pool);
if (rv != APR_SUCCESS)
return rv;
rv = dir_make_parent(dir, perm, pool); /* Make intermediate dirs */
if (rv == APR_SUCCESS || APR_STATUS_IS_EEXIST(rv)) {
rv = apr_dir_make (dir, perm, pool); /* And complete the path */
if (APR_STATUS_IS_EEXIST(rv)) {
rv = APR_SUCCESS; /* Timing issue; see comment below */
}
}
}
else if (APR_STATUS_IS_EEXIST(rv)) {
/*
* It's OK if PATH exists. Timing issues can lead to the
* second apr_dir_make being called on existing dir, therefore
* this check has to come last.
*/
rv = APR_SUCCESS;
}
return rv;
}
APR_DECLARE(apr_status_t) apr_dir_remove(const char *path, apr_pool_t *pool)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wpath,
sizeof(wpath) / sizeof(apr_wchar_t),
path))) {
return rv;
}
if (!RemoveDirectoryW(wpath)) {
return apr_get_os_error();
}
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
if (!RemoveDirectory(path)) {
return apr_get_os_error();
}
#endif
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_os_dir_get(apr_os_dir_t **thedir,
apr_dir_t *dir)
{
if (dir == NULL) {
return APR_ENODIR;
}
*thedir = dir->dirhand;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_os_dir_put(apr_dir_t **dir,
apr_os_dir_t *thedir,
apr_pool_t *pool)
{
return APR_ENOTIMPL;
}
+229
View File
@@ -0,0 +1,229 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_general.h"
#include "apr_strings.h"
#include <string.h>
#include "apr_arch_inherit.h"
#include <io.h> /* for [_open/_get]_osfhandle */
APR_DECLARE(apr_status_t) apr_file_dup(apr_file_t **new_file,
apr_file_t *old_file, apr_pool_t *p)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
HANDLE hproc = GetCurrentProcess();
HANDLE newhand = NULL;
if (!DuplicateHandle(hproc, old_file->filehand,
hproc, &newhand, 0, FALSE,
DUPLICATE_SAME_ACCESS)) {
return apr_get_os_error();
}
(*new_file) = (apr_file_t *) apr_pcalloc(p, sizeof(apr_file_t));
(*new_file)->filehand = newhand;
(*new_file)->flags = old_file->flags & ~(APR_STD_FLAGS | APR_INHERIT);
(*new_file)->pool = p;
(*new_file)->fname = apr_pstrdup(p, old_file->fname);
(*new_file)->append = old_file->append;
(*new_file)->buffered = FALSE;
(*new_file)->ungetchar = old_file->ungetchar;
#if APR_HAS_THREADS
if (old_file->mutex) {
apr_thread_mutex_create(&((*new_file)->mutex),
APR_THREAD_MUTEX_DEFAULT, p);
}
#endif
apr_pool_cleanup_register((*new_file)->pool, (void *)(*new_file), file_cleanup,
apr_pool_cleanup_null);
#if APR_FILES_AS_SOCKETS
/* Create a pollset with room for one descriptor. */
/* ### check return codes */
(void) apr_pollset_create(&(*new_file)->pollset, 1, p, 0);
#endif
return APR_SUCCESS;
#endif /* !defined(_WIN32_WCE) */
}
APR_DECLARE(apr_status_t) apr_file_dup2(apr_file_t *new_file,
apr_file_t *old_file, apr_pool_t *p)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
HANDLE hproc = GetCurrentProcess();
HANDLE newhand = NULL;
apr_int32_t newflags;
int fd;
if (new_file->flags & APR_STD_FLAGS)
{
if ((new_file->flags & APR_STD_FLAGS) == APR_STDERR_FLAG)
{
/* Flush stderr and unset its buffer, then commit the fd-based buffer.
* This is typically a noop for Win2K/XP since services with NULL std
* handles [but valid FILE *'s, oddly enough], but is required
* for NT 4.0 and to use this code outside of services.
*/
fflush(stderr);
setvbuf(stderr, NULL, _IONBF, 0);
if (!_isatty(2)) {
_commit(2 /* stderr */);
}
/* Clone a handle can _close() without harming the source handle,
* open an MSVCRT-based pseudo-fd for the file handle, then dup2
* and close our temporary pseudo-fd once it's been duplicated.
* This will incidently keep the FILE-based stderr in sync.
* Note the apparently redundant _O_BINARY coersions are required.
* Note the _dup2 will close the previous std Win32 handle.
*/
if (!DuplicateHandle(hproc, old_file->filehand, hproc, &newhand,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
return apr_get_os_error();
}
fd = _open_osfhandle((INT_PTR)newhand, _O_WRONLY | _O_BINARY);
_dup2(fd, 2);
_close(fd);
_setmode(2, _O_BINARY);
/* hPipeWrite was _close()'ed above, and _dup2()'ed
* to fd 2 creating a new, inherited Win32 handle.
* Recover that real handle from fd 2. Note that
* SetStdHandle(STD_ERROR_HANDLE, _get_osfhandle(2))
* is implicit in the dup2() call above
*/
newhand = (HANDLE)_get_osfhandle(2);
}
else if ((new_file->flags & APR_STD_FLAGS) == APR_STDOUT_FLAG) {
/* For the process flow see the stderr case above */
fflush(stdout);
setvbuf(stdout, NULL, _IONBF, 0);
if (!_isatty(1)) {
_commit(1 /* stdout */);
}
if (!DuplicateHandle(hproc, old_file->filehand, hproc, &newhand,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
return apr_get_os_error();
}
fd = _open_osfhandle((INT_PTR)newhand, _O_WRONLY | _O_BINARY);
_dup2(fd, 1);
_close(fd);
_setmode(1, _O_BINARY);
newhand = (HANDLE)_get_osfhandle(1);
}
else if ((new_file->flags & APR_STD_FLAGS) == APR_STDIN_FLAG) {
/* For the process flow see the stderr case above */
fflush(stdin);
setvbuf(stdin, NULL, _IONBF, 0);
if (!DuplicateHandle(hproc, old_file->filehand, hproc, &newhand,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
return apr_get_os_error();
}
fd = _open_osfhandle((INT_PTR)newhand, _O_RDONLY | _O_BINARY);
_dup2(fd, 0);
_close(fd);
_setmode(0, _O_BINARY);
newhand = (HANDLE)_get_osfhandle(0);
}
newflags = (new_file->flags & APR_STD_FLAGS)
| (old_file->flags & ~APR_STD_FLAGS) | APR_INHERIT;
/* No need to close the old file, _dup2() above did that for us */
}
else {
if (!DuplicateHandle(hproc, old_file->filehand,
hproc, &newhand, 0,
FALSE, DUPLICATE_SAME_ACCESS)) {
return apr_get_os_error();
}
newflags = old_file->flags & ~(APR_STD_FLAGS | APR_INHERIT);
if (new_file->filehand
&& (new_file->filehand != INVALID_HANDLE_VALUE)) {
CloseHandle(new_file->filehand);
}
}
new_file->flags = newflags;
new_file->filehand = newhand;
new_file->fname = apr_pstrdup(new_file->pool, old_file->fname);
new_file->append = old_file->append;
new_file->buffered = FALSE;
new_file->ungetchar = old_file->ungetchar;
#if APR_HAS_THREADS
if (old_file->mutex) {
apr_thread_mutex_create(&(new_file->mutex),
APR_THREAD_MUTEX_DEFAULT, p);
}
#endif
return APR_SUCCESS;
#endif /* !defined(_WIN32_WCE) */
}
APR_DECLARE(apr_status_t) apr_file_setaside(apr_file_t **new_file,
apr_file_t *old_file,
apr_pool_t *p)
{
*new_file = (apr_file_t *)apr_pmemdup(p, old_file, sizeof(apr_file_t));
(*new_file)->pool = p;
if (old_file->buffered) {
(*new_file)->buffer = apr_palloc(p, old_file->bufsize);
(*new_file)->bufsize = old_file->bufsize;
if (old_file->direction == 1) {
memcpy((*new_file)->buffer, old_file->buffer, old_file->bufpos);
}
else {
memcpy((*new_file)->buffer, old_file->buffer, old_file->dataRead);
}
}
if (old_file->mutex) {
apr_thread_mutex_create(&((*new_file)->mutex),
APR_THREAD_MUTEX_DEFAULT, p);
apr_thread_mutex_destroy(old_file->mutex);
}
if (old_file->fname) {
(*new_file)->fname = apr_pstrdup(p, old_file->fname);
}
if (!(old_file->flags & APR_FOPEN_NOCLEANUP)) {
apr_pool_cleanup_register(p, (void *)(*new_file),
file_cleanup,
file_cleanup);
}
old_file->filehand = INVALID_HANDLE_VALUE;
apr_pool_cleanup_kill(old_file->pool, (void *)old_file,
file_cleanup);
#if APR_FILES_AS_SOCKETS
/* Create a pollset with room for one descriptor. */
/* ### check return codes */
(void) apr_pollset_create(&(*new_file)->pollset, 1, p, 0);
#endif
return APR_SUCCESS;
}
File diff suppressed because it is too large Load Diff
+825
View File
@@ -0,0 +1,825 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr.h"
#include <aclapi.h>
#include "apr_private.h"
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_errno.h"
#include "apr_time.h"
#include <sys/stat.h>
#include "apr_arch_atime.h"
#include "apr_arch_misc.h"
/* We have to assure that the file name contains no '*'s, or other
* wildcards when using FindFirstFile to recover the true file name.
*/
static apr_status_t test_safe_name(const char *name)
{
/* Only accept ':' in the second position of the filename,
* as the drive letter delimiter:
*/
if (apr_isalpha(*name) && (name[1] == ':')) {
name += 2;
}
while (*name) {
if (!IS_FNCHAR(*name) && (*name != '\\') && (*name != '/')) {
if (*name == '?' || *name == '*')
return APR_EPATHWILD;
else
return APR_EBADPATH;
}
++name;
}
return APR_SUCCESS;
}
static apr_status_t free_localheap(void *heap) {
LocalFree(heap);
return APR_SUCCESS;
}
static apr_gid_t worldid = NULL;
static void free_world(void)
{
if (worldid) {
FreeSid(worldid);
worldid = NULL;
}
}
/* Left bit shifts from World scope to given scope */
typedef enum prot_scope_e {
prot_scope_world = 0,
prot_scope_group = 4,
prot_scope_user = 8
} prot_scope_e;
static apr_fileperms_t convert_prot(ACCESS_MASK acc, prot_scope_e scope)
{
/* These choices are based on the single filesystem bit that controls
* the given behavior. They are -not- recommended for any set protection
* function, such a function should -set- use GENERIC_READ/WRITE/EXECUTE
*/
apr_fileperms_t prot = 0;
if (acc & FILE_EXECUTE)
prot |= APR_WEXECUTE;
if (acc & FILE_WRITE_DATA)
prot |= APR_WWRITE;
if (acc & FILE_READ_DATA)
prot |= APR_WREAD;
return (prot << scope);
}
static void resolve_prot(apr_finfo_t *finfo, apr_int32_t wanted, PACL dacl)
{
TRUSTEE_W ident = {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID};
ACCESS_MASK acc;
/*
* This function is only invoked for WinNT,
* there is no reason for os_level testing here.
*/
if ((wanted & APR_FINFO_WPROT) && !worldid) {
SID_IDENTIFIER_AUTHORITY SIDAuth = {SECURITY_WORLD_SID_AUTHORITY};
if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, &worldid))
atexit(free_world);
else
worldid = NULL;
}
if ((wanted & APR_FINFO_UPROT) && (finfo->valid & APR_FINFO_USER)) {
ident.TrusteeType = TRUSTEE_IS_USER;
ident.ptstrName = finfo->user;
/* GetEffectiveRightsFromAcl isn't supported under Win9x,
* which shouldn't come as a surprize. Since we are passing
* TRUSTEE_IS_SID, always skip the A->W layer.
*/
if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
finfo->protection |= convert_prot(acc, prot_scope_user);
finfo->valid |= APR_FINFO_UPROT;
}
}
/* Windows NT: did not return group rights.
* Windows 2000 returns group rights information.
* Since WinNT kernels don't follow the unix model of
* group associations, this all all pretty mute.
*/
if ((wanted & APR_FINFO_GPROT) && (finfo->valid & APR_FINFO_GROUP)) {
ident.TrusteeType = TRUSTEE_IS_GROUP;
ident.ptstrName = finfo->group;
if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
finfo->protection |= convert_prot(acc, prot_scope_group);
finfo->valid |= APR_FINFO_GPROT;
}
}
if ((wanted & APR_FINFO_WPROT) && (worldid)) {
ident.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ident.ptstrName = worldid;
if (GetEffectiveRightsFromAclW(dacl, &ident, &acc) == ERROR_SUCCESS) {
finfo->protection |= convert_prot(acc, prot_scope_world);
finfo->valid |= APR_FINFO_WPROT;
}
}
}
static apr_status_t resolve_ident(apr_finfo_t *finfo, const char *fname,
apr_int32_t wanted, apr_pool_t *pool)
{
apr_file_t *thefile = NULL;
apr_status_t rv;
/*
* NT5 (W2K) only supports symlinks in the same manner as mount points.
* This code should eventually take that into account, for now treat
* every reparse point as a symlink...
*
* We must open the file with READ_CONTROL if we plan to retrieve the
* user, group or permissions.
*/
if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
| ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0)
| ((wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
? APR_READCONTROL : 0),
APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
rv = apr_file_info_get(finfo, wanted, thefile);
finfo->filehand = NULL;
apr_file_close(thefile);
}
else if (APR_STATUS_IS_EACCES(rv) && (wanted & (APR_FINFO_PROT
| APR_FINFO_OWNER))) {
/* We have a backup plan. Perhaps we couldn't grab READ_CONTROL?
* proceed without asking for that permission...
*/
if ((rv = apr_file_open(&thefile, fname, APR_OPENINFO
| ((wanted & APR_FINFO_LINK) ? APR_OPENLINK : 0),
APR_OS_DEFAULT, pool)) == APR_SUCCESS) {
rv = apr_file_info_get(finfo, wanted & ~(APR_FINFO_PROT
| APR_FINFO_OWNER),
thefile);
finfo->filehand = NULL;
apr_file_close(thefile);
}
}
if (rv != APR_SUCCESS && rv != APR_INCOMPLETE)
return (rv);
/* We picked up this case above and had opened the link's properties */
if (wanted & APR_FINFO_LINK)
finfo->valid |= APR_FINFO_LINK;
return rv;
}
static apr_status_t guess_protection_bits(apr_finfo_t *finfo,
apr_int32_t wanted)
{
/* Read, write execute for owner. In the Win9x environment, any
* readable file is executable (well, not entirely 100% true, but
* still looking for some cheap logic that would help us here.)
* The same holds on NT if a file doesn't have a DACL (e.g., on FAT)
*/
if (finfo->protection & APR_FREADONLY) {
finfo->protection |= APR_WREAD | APR_WEXECUTE;
}
else {
finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
}
finfo->protection |= (finfo->protection << prot_scope_group)
| (finfo->protection << prot_scope_user);
finfo->valid |= APR_FINFO_UPROT | APR_FINFO_GPROT | APR_FINFO_WPROT;
return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
}
apr_status_t more_finfo(apr_finfo_t *finfo, const void *ufile,
apr_int32_t wanted, int whatfile)
{
PSID user = NULL, grp = NULL;
PACL dacl = NULL;
apr_status_t rv;
if (apr_os_level < APR_WIN_NT)
return guess_protection_bits(finfo, wanted);
if (wanted & (APR_FINFO_PROT | APR_FINFO_OWNER))
{
/* On NT this request is incredibly expensive, but accurate.
* Since the WinNT-only functions below are protected by the
* (apr_os_level < APR_WIN_NT) case above, we need no extra
* tests, but remember GetNamedSecurityInfo & GetSecurityInfo
* are not supported on 9x.
*/
SECURITY_INFORMATION sinf = 0;
PSECURITY_DESCRIPTOR pdesc = NULL;
if (wanted & (APR_FINFO_USER | APR_FINFO_UPROT))
sinf |= OWNER_SECURITY_INFORMATION;
if (wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT))
sinf |= GROUP_SECURITY_INFORMATION;
if (wanted & APR_FINFO_PROT)
sinf |= DACL_SECURITY_INFORMATION;
if (whatfile == MORE_OF_WFSPEC) {
apr_wchar_t *wfile = (apr_wchar_t*) ufile;
int fix = 0;
if (wcsncmp(wfile, L"\\\\?\\", 4) == 0) {
fix = 4;
if (wcsncmp(wfile + fix, L"UNC\\", 4) == 0)
wfile[6] = L'\\', fix = 6;
}
rv = GetNamedSecurityInfoW(wfile + fix,
SE_FILE_OBJECT, sinf,
((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
((wanted & APR_FINFO_PROT) ? &dacl : NULL),
NULL, &pdesc);
if (fix == 6)
wfile[6] = L'C';
}
else if (whatfile == MORE_OF_FSPEC)
rv = GetNamedSecurityInfoA((char*)ufile,
SE_FILE_OBJECT, sinf,
((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
((wanted & APR_FINFO_PROT) ? &dacl : NULL),
NULL, &pdesc);
else if (whatfile == MORE_OF_HANDLE)
rv = GetSecurityInfo((HANDLE)ufile,
SE_FILE_OBJECT, sinf,
((wanted & (APR_FINFO_USER | APR_FINFO_UPROT)) ? &user : NULL),
((wanted & (APR_FINFO_GROUP | APR_FINFO_GPROT)) ? &grp : NULL),
((wanted & APR_FINFO_PROT) ? &dacl : NULL),
NULL, &pdesc);
else
return APR_INCOMPLETE; /* should not occur */
if (rv == ERROR_SUCCESS)
apr_pool_cleanup_register(finfo->pool, pdesc, free_localheap,
apr_pool_cleanup_null);
else
user = grp = dacl = NULL;
if (user) {
finfo->user = user;
finfo->valid |= APR_FINFO_USER;
}
if (grp) {
finfo->group = grp;
finfo->valid |= APR_FINFO_GROUP;
}
if (dacl) {
/* Retrieved the discresionary access list */
resolve_prot(finfo, wanted, dacl);
}
else if (wanted & APR_FINFO_PROT)
guess_protection_bits(finfo, wanted);
}
if ((apr_os_level >= APR_WIN_2000) && (wanted & APR_FINFO_CSIZE)
&& (finfo->filetype == APR_REG))
{
DWORD sizelo, sizehi;
if (whatfile == MORE_OF_HANDLE) {
/* Not available for development and implementation under
* a reasonable license; if you review the licensing
* terms and conditions of;
* http://go.microsoft.com/fwlink/?linkid=84083
* you probably understand why APR chooses not to implement.
*/
IOSB sb;
FSI fi;
if ((ZwQueryInformationFile((HANDLE)ufile, &sb,
&fi, sizeof(fi), 5) == 0)
&& (sb.Status == 0)) {
finfo->csize = fi.AllocationSize;
finfo->valid |= APR_FINFO_CSIZE;
}
}
else {
SetLastError(NO_ERROR);
if (whatfile == MORE_OF_WFSPEC)
sizelo = GetCompressedFileSizeW((apr_wchar_t*)ufile, &sizehi);
else if (whatfile == MORE_OF_FSPEC)
sizelo = GetCompressedFileSizeA((char*)ufile, &sizehi);
else
return APR_EGENERAL; /* should not occur */
if (sizelo != INVALID_FILE_SIZE || GetLastError() == NO_ERROR) {
#if APR_HAS_LARGE_FILES
finfo->csize = (apr_off_t)sizelo
| ((apr_off_t)sizehi << 32);
#else
finfo->csize = (apr_off_t)sizelo;
if (finfo->csize < 0 || sizehi)
finfo->csize = 0x7fffffff;
#endif
finfo->valid |= APR_FINFO_CSIZE;
}
}
}
return ((wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS);
}
/* This generic fillin depends upon byhandle to be passed as 0 when
* a WIN32_FILE_ATTRIBUTE_DATA or either WIN32_FIND_DATA [A or W] is
* passed for wininfo. When the BY_HANDLE_FILE_INFORMATION structure
* is passed for wininfo, byhandle is passed as 1 to offset the one
* dword discrepancy in offset of the High/Low size structure members.
*
* The generic fillin returns 1 if the caller should further inquire
* if this is a CHR filetype. If it's reasonably certain it can't be,
* then the function returns 0.
*/
int fillin_fileinfo(apr_finfo_t *finfo,
WIN32_FILE_ATTRIBUTE_DATA *wininfo,
int byhandle, apr_int32_t wanted)
{
DWORD *sizes = &wininfo->nFileSizeHigh + byhandle;
int warn = 0;
memset(finfo, '\0', sizeof(*finfo));
FileTimeToAprTime(&finfo->atime, &wininfo->ftLastAccessTime);
FileTimeToAprTime(&finfo->ctime, &wininfo->ftCreationTime);
FileTimeToAprTime(&finfo->mtime, &wininfo->ftLastWriteTime);
#if APR_HAS_LARGE_FILES
finfo->size = (apr_off_t)sizes[1]
| ((apr_off_t)sizes[0] << 32);
#else
finfo->size = (apr_off_t)sizes[1];
if (finfo->size < 0 || sizes[0])
finfo->size = 0x7fffffff;
#endif
if (wanted & APR_FINFO_LINK &&
wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
finfo->filetype = APR_LNK;
}
else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
finfo->filetype = APR_DIR;
}
else if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
/* Warning: This test only succeeds on Win9x, on NT these files
* (con, aux, nul, lpt#, com# etc) escape early detection!
*/
finfo->filetype = APR_CHR;
}
else {
/* Warning: Short of opening the handle to the file, the 'FileType'
* appears to be unknowable (in any trustworthy or consistent sense)
* on WinNT/2K as far as PIPE, CHR, etc are concerned.
*/
if (!wininfo->ftLastWriteTime.dwLowDateTime
&& !wininfo->ftLastWriteTime.dwHighDateTime
&& !finfo->size)
warn = 1;
finfo->filetype = APR_REG;
}
/* The following flags are [for this moment] private to Win32.
* That's the only excuse for not toggling valid bits to reflect them.
*/
if (wininfo->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
finfo->protection = APR_FREADONLY;
finfo->valid = APR_FINFO_ATIME | APR_FINFO_CTIME | APR_FINFO_MTIME
| APR_FINFO_SIZE | APR_FINFO_TYPE; /* == APR_FINFO_MIN */
/* Only byhandle optionally tests link targets, so tell that caller
* what it wants to hear, otherwise the byattributes is never
* reporting anything but the link.
*/
if (!byhandle || (wanted & APR_FINFO_LINK))
finfo->valid |= APR_FINFO_LINK;
return warn;
}
APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
apr_file_t *thefile)
{
BY_HANDLE_FILE_INFORMATION FileInfo;
if (thefile->buffered) {
/* XXX: flush here is not mutex protected */
apr_status_t rv = apr_file_flush(thefile);
if (rv != APR_SUCCESS)
return rv;
}
/* GetFileInformationByHandle() is implemented via two syscalls:
* QueryInformationVolume and QueryAllInformationFile. Use cheaper
* GetFileSizeEx() API if we only need to get the file size. */
if (wanted == APR_FINFO_SIZE) {
LARGE_INTEGER size;
if (!GetFileSizeEx(thefile->filehand, &size)) {
return apr_get_os_error();
}
finfo->pool = thefile->pool;
finfo->fname = thefile->fname;
finfo->size = size.QuadPart;
finfo->valid = APR_FINFO_SIZE;
return APR_SUCCESS;
}
if (!GetFileInformationByHandle(thefile->filehand, &FileInfo)) {
return apr_get_os_error();
}
fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo, 1, wanted);
if (finfo->filetype == APR_REG)
{
/* Go the extra mile to be -certain- that we have a real, regular
* file, since the attribute bits aren't a certain thing. Even
* though fillin should have hinted if we *must* do this, we
* don't need to take chances while the handle is already open.
*/
DWORD FileType;
if ((FileType = GetFileType(thefile->filehand))) {
if (FileType == FILE_TYPE_CHAR) {
finfo->filetype = APR_CHR;
}
else if (FileType == FILE_TYPE_PIPE) {
finfo->filetype = APR_PIPE;
}
/* Otherwise leave the original conclusion alone
*/
}
}
finfo->pool = thefile->pool;
/* ### The finfo lifetime may exceed the lifetime of thefile->pool
* but finfo's aren't managed in pools, so where on earth would
* we pstrdup the fname into???
*/
finfo->fname = thefile->fname;
/* Extra goodies known only by GetFileInformationByHandle() */
finfo->inode = (apr_ino_t)FileInfo.nFileIndexLow
| ((apr_ino_t)FileInfo.nFileIndexHigh << 32);
finfo->device = FileInfo.dwVolumeSerialNumber;
finfo->nlink = FileInfo.nNumberOfLinks;
finfo->valid |= APR_FINFO_IDENT | APR_FINFO_NLINK;
/* If we still want something more (besides the name) go get it!
*/
if ((wanted &= ~finfo->valid) & ~APR_FINFO_NAME) {
return more_finfo(finfo, thefile->filehand, wanted, MORE_OF_HANDLE);
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
apr_fileperms_t perms)
{
return APR_ENOTIMPL;
}
APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, const char *fname,
apr_int32_t wanted, apr_pool_t *pool)
{
/* XXX: is constant - needs testing - which requires a lighter-weight root test fn */
int isroot = 0;
apr_status_t ident_rv = 0;
apr_status_t rv;
#if APR_HAS_UNICODE_FS
apr_wchar_t wfname[APR_PATH_MAX];
#endif
char *filename = NULL;
/* These all share a common subset of this structure */
union {
WIN32_FIND_DATAW w;
WIN32_FIND_DATAA n;
WIN32_FILE_ATTRIBUTE_DATA i;
} FileInfo;
/* Catch fname length == MAX_PATH since GetFileAttributesEx fails
* with PATH_NOT_FOUND. We would rather indicate length error than
* 'not found'
*/
if (strlen(fname) >= APR_PATH_MAX) {
return APR_ENAMETOOLONG;
}
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
if ((wanted & (APR_FINFO_IDENT | APR_FINFO_NLINK))
|| (~wanted & APR_FINFO_LINK)) {
/* FindFirstFile and GetFileAttributesEx can't figure the inode,
* device or number of links, so we need to resolve with an open
* file handle. If the user has asked for these fields, fall over
* to the get file info by handle method. If we fail, or the user
* also asks for the file name, continue by our usual means.
*
* We also must use this method for a 'true' stat, that resolves
* a symlink (NTFS Junction) target. This is because all fileinfo
* on a Junction always returns the junction, opening the target
* is the only way to resolve the target's attributes.
*/
if ((ident_rv = resolve_ident(finfo, fname, wanted, pool))
== APR_SUCCESS)
return ident_rv;
else if (ident_rv == APR_INCOMPLETE)
wanted &= ~finfo->valid;
}
if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname)
/ sizeof(apr_wchar_t), fname)))
return rv;
if (!(wanted & APR_FINFO_NAME)) {
if (!GetFileAttributesExW(wfname, GetFileExInfoStandard,
&FileInfo.i))
return apr_get_os_error();
}
else {
/* Guard against bogus wildcards and retrieve by name
* since we want the true name, and set aside a long
* enough string to handle the longest file name.
*/
char tmpname[APR_FILE_MAX * 3 + 1];
HANDLE hFind;
if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
return rv;
}
hFind = FindFirstFileW(wfname, &FileInfo.w);
if (hFind == INVALID_HANDLE_VALUE)
return apr_get_os_error();
FindClose(hFind);
if (unicode_to_utf8_path(tmpname, sizeof(tmpname),
FileInfo.w.cFileName)) {
return APR_ENAMETOOLONG;
}
filename = apr_pstrdup(pool, tmpname);
}
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
char *root = NULL;
const char *test = fname;
rv = apr_filepath_root(&root, &test, APR_FILEPATH_NATIVE, pool);
isroot = (root && *root && !(*test));
if ((apr_os_level >= APR_WIN_98) && (!(wanted & APR_FINFO_NAME) || isroot))
{
/* cannot use FindFile on a Win98 root, it returns \*
* GetFileAttributesExA is not available on Win95
*/
if (!GetFileAttributesExA(fname, GetFileExInfoStandard,
&FileInfo.i)) {
return apr_get_os_error();
}
}
else if (isroot) {
/* This is Win95 and we are trying to stat a root. Lie.
*/
if (GetDriveType(fname) != DRIVE_UNKNOWN)
{
finfo->pool = pool;
finfo->filetype = 0;
finfo->mtime = apr_time_now();
finfo->protection |= APR_WREAD | APR_WEXECUTE | APR_WWRITE;
finfo->protection |= (finfo->protection << prot_scope_group)
| (finfo->protection << prot_scope_user);
finfo->valid |= APR_FINFO_TYPE | APR_FINFO_PROT
| APR_FINFO_MTIME
| (wanted & APR_FINFO_LINK);
return (wanted &= ~finfo->valid) ? APR_INCOMPLETE
: APR_SUCCESS;
}
else
return APR_FROM_OS_ERROR(ERROR_PATH_NOT_FOUND);
}
else {
/* Guard against bogus wildcards and retrieve by name
* since we want the true name, or are stuck in Win95,
* or are looking for the root of a Win98 drive.
*/
HANDLE hFind;
if ((rv = test_safe_name(fname)) != APR_SUCCESS) {
return rv;
}
hFind = FindFirstFileA(fname, &FileInfo.n);
if (hFind == INVALID_HANDLE_VALUE) {
return apr_get_os_error();
}
FindClose(hFind);
filename = apr_pstrdup(pool, FileInfo.n.cFileName);
}
}
#endif
if (ident_rv != APR_INCOMPLETE) {
if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo,
0, wanted))
{
/* Go the extra mile to assure we have a file. WinNT/2000 seems
* to reliably translate char devices to the path '\\.\device'
* so go ask for the full path.
*/
if (apr_os_level >= APR_WIN_NT)
{
#if APR_HAS_UNICODE_FS
apr_wchar_t tmpname[APR_FILE_MAX];
apr_wchar_t *tmpoff = NULL;
if (GetFullPathNameW(wfname, sizeof(tmpname) / sizeof(apr_wchar_t),
tmpname, &tmpoff))
{
if (!wcsncmp(tmpname, L"\\\\.\\", 4)) {
#else
/* Same initial logic as above, but
* only for WinNT/non-UTF-8 builds of APR:
*/
char tmpname[APR_FILE_MAX];
char *tmpoff;
if (GetFullPathName(fname, sizeof(tmpname), tmpname, &tmpoff))
{
if (!strncmp(tmpname, "\\\\.\\", 4)) {
#endif
if (tmpoff == tmpname + 4) {
finfo->filetype = APR_CHR;
}
/* For WHATEVER reason, CHR devices such as \\.\con
* or \\.\lpt1 *may*not* update tmpoff; in fact the
* resulting tmpoff is set to NULL. Guard against
* either case.
*
* This code is identical for wide and narrow chars...
*/
else if (!tmpoff) {
tmpoff = tmpname + 4;
while (*tmpoff) {
if (*tmpoff == '\\' || *tmpoff == '/') {
break;
}
++tmpoff;
}
if (!*tmpoff) {
finfo->filetype = APR_CHR;
}
}
}
}
else {
finfo->valid &= ~APR_FINFO_TYPE;
}
}
else {
finfo->valid &= ~APR_FINFO_TYPE;
}
}
finfo->pool = pool;
}
if (filename && !isroot) {
finfo->name = filename;
finfo->valid |= APR_FINFO_NAME;
}
if (wanted &= ~finfo->valid) {
/* Caller wants more than APR_FINFO_MIN | APR_FINFO_NAME */
#if APR_HAS_UNICODE_FS
if (apr_os_level >= APR_WIN_NT)
return more_finfo(finfo, wfname, wanted, MORE_OF_WFSPEC);
#endif
return more_finfo(finfo, fname, wanted, MORE_OF_FSPEC);
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_attrs_set(const char *fname,
apr_fileattrs_t attributes,
apr_fileattrs_t attr_mask,
apr_pool_t *pool)
{
DWORD flags;
apr_status_t rv;
#if APR_HAS_UNICODE_FS
apr_wchar_t wfname[APR_PATH_MAX];
#endif
/* Don't do anything if we can't handle the requested attributes */
if (!(attr_mask & (APR_FILE_ATTR_READONLY
| APR_FILE_ATTR_HIDDEN)))
return APR_SUCCESS;
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
if ((rv = utf8_to_unicode_path(wfname,
sizeof(wfname) / sizeof(wfname[0]),
fname)))
return rv;
flags = GetFileAttributesW(wfname);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
flags = GetFileAttributesA(fname);
}
#endif
if (flags == 0xFFFFFFFF)
return apr_get_os_error();
if (attr_mask & APR_FILE_ATTR_READONLY)
{
if (attributes & APR_FILE_ATTR_READONLY)
flags |= FILE_ATTRIBUTE_READONLY;
else
flags &= ~FILE_ATTRIBUTE_READONLY;
}
if (attr_mask & APR_FILE_ATTR_HIDDEN)
{
if (attributes & APR_FILE_ATTR_HIDDEN)
flags |= FILE_ATTRIBUTE_HIDDEN;
else
flags &= ~FILE_ATTRIBUTE_HIDDEN;
}
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
rv = SetFileAttributesW(wfname, flags);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
rv = SetFileAttributesA(fname, flags);
}
#endif
if (rv == 0)
return apr_get_os_error();
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
apr_time_t mtime,
apr_pool_t *pool)
{
apr_file_t *thefile;
apr_status_t rv;
rv = apr_file_open(&thefile, fname,
APR_FOPEN_READ | APR_WRITEATTRS,
APR_OS_DEFAULT, pool);
if (!rv)
{
FILETIME file_ctime;
FILETIME file_atime;
FILETIME file_mtime;
if (!GetFileTime(thefile->filehand,
&file_ctime, &file_atime, &file_mtime))
rv = apr_get_os_error();
else
{
AprTimeToFileTime(&file_mtime, mtime);
if (!SetFileTime(thefile->filehand,
&file_ctime, &file_atime, &file_mtime))
rv = apr_get_os_error();
}
apr_file_close(thefile);
}
return rv;
}
+229
View File
@@ -0,0 +1,229 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr.h"
#include "apr_arch_file_io.h"
#include "apr_strings.h"
/* Win32 Exceptions:
*
* Note that trailing spaces and trailing periods are never recorded
* in the file system, except by a very obscure bug where any file
* that is created with a trailing space or period, followed by the
* ':' stream designator on an NTFS volume can never be accessed again.
* In other words, don't ever accept them when designating a stream!
*
* An interesting side effect is that two or three periods are both
* treated as the parent directory, although the fourth and on are
* not [strongly suggest all trailing periods are trimmed off, or
* down to two if there are no other characters.]
*
* Leading spaces and periods are accepted, however.
* The * ? < > codes all have wildcard side effects
* The " / \ : are exclusively component separator tokens
* The system doesn't accept | for any (known) purpose
* Oddly, \x7f _is_ acceptable ;)
*/
/* apr_c_is_fnchar[] maps Win32's file name and shell escape symbols
*
* element & 1 == valid file name character [excluding delimiters]
* element & 2 == character should be shell (caret) escaped from cmd.exe
*
* this must be in-sync with Apache httpd's gen_test_char.c for cgi escaping.
*/
const char apr_c_is_fnchar[256] =
{/* Reject all ctrl codes... Escape \n and \r (ascii 10 and 13) */
0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
1,1,2,1,3,3,3,3,3,3,2,1,1,1,1,0, 1,1,1,1,1,1,1,1,1,1,0,3,2,1,2,2,
/* @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,3,2,3,3,1,
/* ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ */
3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,3,2,3,3,1,
/* High bit codes are accepted (subject to utf-8->Unicode xlation) */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
};
apr_status_t filepath_root_test(char *path, apr_pool_t *p)
{
apr_status_t rv;
#if APR_HAS_UNICODE_FS
if (apr_os_level >= APR_WIN_NT)
{
apr_wchar_t wpath[APR_PATH_MAX];
if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath)
/ sizeof(apr_wchar_t), path)))
return rv;
rv = GetDriveTypeW(wpath);
}
else
#endif
rv = GetDriveType(path);
if (rv == DRIVE_UNKNOWN || rv == DRIVE_NO_ROOT_DIR)
return APR_EBADPATH;
return APR_SUCCESS;
}
apr_status_t filepath_drive_get(char **rootpath, char drive,
apr_int32_t flags, apr_pool_t *p)
{
char path[APR_PATH_MAX];
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t *ignored;
apr_wchar_t wdrive[8];
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
/* ???: This needs review, apparently "\\?\d:." returns "\\?\d:"
* as if that is useful for anything.
*/
wcscpy(wdrive, L"D:.");
wdrive[0] = (apr_wchar_t)(unsigned char)drive;
if (!GetFullPathNameW(wdrive, sizeof(wpath) / sizeof(apr_wchar_t), wpath, &ignored))
return apr_get_os_error();
if ((rv = unicode_to_utf8_path(path, sizeof(path), wpath)))
return rv;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
char *ignored;
char drivestr[4];
drivestr[0] = drive;
drivestr[1] = ':';
drivestr[2] = '.';;
drivestr[3] = '\0';
if (!GetFullPathName(drivestr, sizeof(path), path, &ignored))
return apr_get_os_error();
}
#endif
if (!(flags & APR_FILEPATH_NATIVE)) {
for (*rootpath = path; **rootpath; ++*rootpath) {
if (**rootpath == '\\')
**rootpath = '/';
}
}
*rootpath = apr_pstrdup(p, path);
return APR_SUCCESS;
}
apr_status_t filepath_root_case(char **rootpath, char *root, apr_pool_t *p)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t *ignored;
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
apr_wchar_t wroot[APR_PATH_MAX];
/* ???: This needs review, apparently "\\?\d:." returns "\\?\d:"
* as if that is useful for anything.
*/
if ((rv = utf8_to_unicode_path(wroot, sizeof(wroot)
/ sizeof(apr_wchar_t), root)))
return rv;
if (!GetFullPathNameW(wroot, sizeof(wpath) / sizeof(apr_wchar_t), wpath, &ignored))
return apr_get_os_error();
/* Borrow wroot as a char buffer (twice as big as necessary)
*/
if ((rv = unicode_to_utf8_path((char*)wroot, sizeof(wroot), wpath)))
return rv;
*rootpath = apr_pstrdup(p, (char*)wroot);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
char path[APR_PATH_MAX];
char *ignored;
if (!GetFullPathName(root, sizeof(path), path, &ignored))
return apr_get_os_error();
*rootpath = apr_pstrdup(p, path);
}
#endif
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_filepath_get(char **rootpath, apr_int32_t flags,
apr_pool_t *p)
{
char path[APR_PATH_MAX];
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
if (!GetCurrentDirectoryW(sizeof(wpath) / sizeof(apr_wchar_t), wpath))
return apr_get_os_error();
if ((rv = unicode_to_utf8_path(path, sizeof(path), wpath)))
return rv;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
if (!GetCurrentDirectory(sizeof(path), path))
return apr_get_os_error();
}
#endif
if (!(flags & APR_FILEPATH_NATIVE)) {
for (*rootpath = path; **rootpath; ++*rootpath) {
if (**rootpath == '\\')
**rootpath = '/';
}
}
*rootpath = apr_pstrdup(p, path);
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_filepath_set(const char *rootpath,
apr_pool_t *p)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath)
/ sizeof(apr_wchar_t), rootpath)))
return rv;
if (!SetCurrentDirectoryW(wpath))
return apr_get_os_error();
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
if (!SetCurrentDirectory(rootpath))
return apr_get_os_error();
}
#endif
return APR_SUCCESS;
}
+86
View File
@@ -0,0 +1,86 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type)
{
#ifdef _WIN32_WCE
/* The File locking is unsuported on WCE */
return APR_ENOTIMPL;
#else
const DWORD len = 0xffffffff;
DWORD flags;
flags = ((type & APR_FLOCK_NONBLOCK) ? LOCKFILE_FAIL_IMMEDIATELY : 0)
+ (((type & APR_FLOCK_TYPEMASK) == APR_FLOCK_SHARED)
? 0 : LOCKFILE_EXCLUSIVE_LOCK);
if (apr_os_level >= APR_WIN_NT) {
/* Syntax is correct, len is passed for LengthLow and LengthHigh*/
OVERLAPPED offset;
memset (&offset, 0, sizeof(offset));
if (!LockFileEx(thefile->filehand, flags, 0, len, len, &offset))
return apr_get_os_error();
}
else {
/* On Win9x, LockFile() never blocks. Hack in a crufty poll.
*
* Note that this hack exposes threads to being unserviced forever,
* in the situation that the given lock has low availability.
* When implemented in the kernel, LockFile will typically use
* FIFO or round robin distribution to ensure all threads get
* one crack at the lock; but in this case we can't emulate that.
*
* However Win9x are barely maintainable anyways, if the user does
* choose to build to them, this is the best we can do.
*/
while (!LockFile(thefile->filehand, 0, 0, len, 0)) {
DWORD err = GetLastError();
if ((err == ERROR_LOCK_VIOLATION) && !(type & APR_FLOCK_NONBLOCK))
{
Sleep(500); /* pause for a half second */
continue; /* ... and then poll again */
}
return APR_FROM_OS_ERROR(err);
}
}
return APR_SUCCESS;
#endif /* !defined(_WIN32_WCE) */
}
APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
DWORD len = 0xffffffff;
if (apr_os_level >= APR_WIN_NT) {
/* Syntax is correct, len is passed for LengthLow and LengthHigh*/
OVERLAPPED offset;
memset (&offset, 0, sizeof(offset));
if (!UnlockFileEx(thefile->filehand, 0, len, len, &offset))
return apr_get_os_error();
}
else {
if (!UnlockFile(thefile->filehand, 0, 0, len, 0))
return apr_get_os_error();
}
return APR_SUCCESS;
#endif /* !defined(_WIN32_WCE) */
}
+755
View File
@@ -0,0 +1,755 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_private.h"
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_portable.h"
#include "apr_thread_mutex.h"
#if APR_HAVE_ERRNO_H
#include <errno.h>
#endif
#include <winbase.h>
#include <string.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "apr_arch_misc.h"
#include "apr_arch_inherit.h"
#include <io.h>
#include <winioctl.h>
#if APR_HAS_UNICODE_FS
apr_status_t utf8_to_unicode_path(apr_wchar_t* retstr, apr_size_t retlen,
const char* srcstr)
{
/* TODO: The computations could preconvert the string to determine
* the true size of the retstr, but that's a memory over speed
* tradeoff that isn't appropriate this early in development.
*
* Allocate the maximum string length based on leading 4
* characters of \\?\ (allowing nearly unlimited path lengths)
* plus the trailing null, then transform /'s into \\'s since
* the \\?\ form doesn't allow '/' path seperators.
*
* Note that the \\?\ form only works for local drive paths, and
* \\?\UNC\ is needed UNC paths.
*/
apr_size_t srcremains = strlen(srcstr) + 1;
apr_wchar_t *t = retstr;
apr_status_t rv;
/* This is correct, we don't twist the filename if it is will
* definitely be shorter than 248 characters. It merits some
* performance testing to see if this has any effect, but there
* seem to be applications that get confused by the resulting
* Unicode \\?\ style file names, especially if they use argv[0]
* or call the Win32 API functions such as GetModuleName, etc.
* Not every application is prepared to handle such names.
*
* Note also this is shorter than MAX_PATH, as directory paths
* are actually limited to 248 characters.
*
* Note that a utf-8 name can never result in more wide chars
* than the original number of utf-8 narrow chars.
*/
if (srcremains > 248) {
if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
wcscpy (retstr, L"\\\\?\\");
retlen -= 4;
t += 4;
}
else if ((srcstr[0] == '/' || srcstr[0] == '\\')
&& (srcstr[1] == '/' || srcstr[1] == '\\')
&& (srcstr[2] != '?')) {
/* Skip the slashes */
srcstr += 2;
srcremains -= 2;
wcscpy (retstr, L"\\\\?\\UNC\\");
retlen -= 8;
t += 8;
}
}
if ((rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen))) {
return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
}
if (srcremains) {
return APR_ENAMETOOLONG;
}
for (; *t; ++t)
if (*t == L'/')
*t = L'\\';
return APR_SUCCESS;
}
apr_status_t unicode_to_utf8_path(char* retstr, apr_size_t retlen,
const apr_wchar_t* srcstr)
{
/* Skip the leading 4 characters if the path begins \\?\, or substitute
* // for the \\?\UNC\ path prefix, allocating the maximum string
* length based on the remaining string, plus the trailing null.
* then transform \\'s back into /'s since the \\?\ form never
* allows '/' path seperators, and APR always uses '/'s.
*/
apr_size_t srcremains = wcslen(srcstr) + 1;
apr_status_t rv;
char *t = retstr;
if (srcstr[0] == L'\\' && srcstr[1] == L'\\' &&
srcstr[2] == L'?' && srcstr[3] == L'\\') {
if (srcstr[4] == L'U' && srcstr[5] == L'N' &&
srcstr[6] == L'C' && srcstr[7] == L'\\') {
srcremains -= 8;
srcstr += 8;
retstr[0] = '\\';
retstr[1] = '\\';
retlen -= 2;
t += 2;
}
else {
srcremains -= 4;
srcstr += 4;
}
}
if ((rv = apr_conv_ucs2_to_utf8(srcstr, &srcremains, t, &retlen))) {
return rv;
}
if (srcremains) {
return APR_ENAMETOOLONG;
}
return APR_SUCCESS;
}
#endif
void *res_name_from_filename(const char *file, int global, apr_pool_t *pool)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t *wpre, *wfile, *ch;
apr_size_t n = strlen(file) + 1;
apr_size_t r, d;
if (apr_os_level >= APR_WIN_2000) {
if (global)
wpre = L"Global\\";
else
wpre = L"Local\\";
}
else
wpre = L"";
r = wcslen(wpre);
if (n > 256 - r) {
file += n - 256 - r;
n = 256;
/* skip utf8 continuation bytes */
while ((*file & 0xC0) == 0x80) {
++file;
--n;
}
}
wfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
wcscpy(wfile, wpre);
d = n;
if (apr_conv_utf8_to_ucs2(file, &n, wfile + r, &d)) {
return NULL;
}
for (ch = wfile + r; *ch; ++ch) {
if (*ch == ':' || *ch == '/' || *ch == '\\')
*ch = '_';
}
return wfile;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
char *nfile, *ch;
apr_size_t n = strlen(file) + 1;
#if !APR_HAS_UNICODE_FS
apr_size_t r, d;
char *pre;
if (apr_os_level >= APR_WIN_2000) {
if (global)
pre = "Global\\";
else
pre = "Local\\";
}
else
pre = "";
r = strlen(pre);
if (n > 256 - r) {
file += n - 256 - r;
n = 256;
}
nfile = apr_palloc(pool, (r + n) * sizeof(apr_wchar_t));
memcpy(nfile, pre, r);
memcpy(nfile + r, file, n);
#else
const apr_size_t r = 0;
if (n > 256) {
file += n - 256;
n = 256;
}
nfile = apr_pmemdup(pool, file, n);
#endif
for (ch = nfile + r; *ch; ++ch) {
if (*ch == ':' || *ch == '/' || *ch == '\\')
*ch = '_';
}
return nfile;
}
#endif
}
#if APR_HAS_UNICODE_FS
static apr_status_t make_sparse_file(apr_file_t *file)
{
BY_HANDLE_FILE_INFORMATION info;
apr_status_t rv;
DWORD bytesread = 0;
DWORD res;
/* test */
if (GetFileInformationByHandle(file->filehand, &info)
&& (info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE))
return APR_SUCCESS;
if (file->pOverlapped) {
file->pOverlapped->Offset = 0;
file->pOverlapped->OffsetHigh = 0;
}
if (DeviceIoControl(file->filehand, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
&bytesread, file->pOverlapped)) {
rv = APR_SUCCESS;
}
else
{
rv = apr_get_os_error();
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING))
{
do {
res = WaitForSingleObject(file->pOverlapped->hEvent,
(file->timeout > 0)
? (DWORD)(file->timeout/1000)
: ((file->timeout == -1)
? INFINITE : 0));
} while (res == WAIT_ABANDONED);
if (res != WAIT_OBJECT_0) {
CancelIo(file->filehand);
}
if (GetOverlappedResult(file->filehand, file->pOverlapped,
&bytesread, TRUE))
rv = APR_SUCCESS;
else
rv = apr_get_os_error();
}
}
return rv;
}
#endif
apr_status_t file_cleanup(void *thefile)
{
apr_file_t *file = thefile;
apr_status_t flush_rv = APR_SUCCESS;
if (file->filehand != INVALID_HANDLE_VALUE) {
if (file->buffered) {
/* XXX: flush here is not mutex protected */
flush_rv = apr_file_flush((apr_file_t *)thefile);
}
/* In order to avoid later segfaults with handle 'reuse',
* we must protect against the case that a dup2'ed handle
* is being closed, and invalidate the corresponding StdHandle
* We also tell msvcrt when stdhandles are closed.
*/
if (file->flags & APR_STD_FLAGS)
{
if ((file->flags & APR_STD_FLAGS) == APR_STDERR_FLAG) {
_close(2);
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
}
else if ((file->flags & APR_STD_FLAGS) == APR_STDOUT_FLAG) {
_close(1);
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
}
else if ((file->flags & APR_STD_FLAGS) == APR_STDIN_FLAG) {
_close(0);
SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE);
}
}
else
CloseHandle(file->filehand);
file->filehand = INVALID_HANDLE_VALUE;
}
if (file->pOverlapped && file->pOverlapped->hEvent) {
CloseHandle(file->pOverlapped->hEvent);
file->pOverlapped = NULL;
}
return flush_rv;
}
APR_DECLARE(apr_status_t) apr_file_open(apr_file_t **new, const char *fname,
apr_int32_t flag, apr_fileperms_t perm,
apr_pool_t *pool)
{
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD oflags = 0;
DWORD createflags = 0;
DWORD attributes = 0;
DWORD sharemode = FILE_SHARE_READ | FILE_SHARE_WRITE;
apr_status_t rv;
if (flag & APR_FOPEN_NONBLOCK) {
return APR_ENOTIMPL;
}
if (flag & APR_FOPEN_READ) {
oflags |= GENERIC_READ;
}
if (flag & APR_FOPEN_WRITE) {
oflags |= GENERIC_WRITE;
}
if (flag & APR_WRITEATTRS) {
oflags |= FILE_WRITE_ATTRIBUTES;
}
if (apr_os_level >= APR_WIN_NT)
sharemode |= FILE_SHARE_DELETE;
if (flag & APR_FOPEN_CREATE) {
if (flag & APR_FOPEN_EXCL) {
/* only create new if file does not already exist */
createflags = CREATE_NEW;
} else if (flag & APR_FOPEN_TRUNCATE) {
/* truncate existing file or create new */
createflags = CREATE_ALWAYS;
} else {
/* open existing but create if necessary */
createflags = OPEN_ALWAYS;
}
} else if (flag & APR_FOPEN_TRUNCATE) {
/* only truncate if file already exists */
createflags = TRUNCATE_EXISTING;
} else {
/* only open if file already exists */
createflags = OPEN_EXISTING;
}
if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
return APR_EACCES;
}
if (flag & APR_FOPEN_DELONCLOSE) {
attributes |= FILE_FLAG_DELETE_ON_CLOSE;
}
if (flag & APR_OPENLINK) {
attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
}
/* Without READ or WRITE, we fail unless apr called apr_file_open
* internally with the private APR_OPENINFO flag.
*
* With the APR_OPENINFO flag on NT, use the option flag
* FILE_FLAG_BACKUP_SEMANTICS to allow us to open directories.
* See the static resolve_ident() fn in file_io/win32/filestat.c
*/
if (!(flag & (APR_FOPEN_READ | APR_FOPEN_WRITE))) {
if (flag & APR_OPENINFO) {
if (apr_os_level >= APR_WIN_NT) {
attributes |= FILE_FLAG_BACKUP_SEMANTICS;
}
}
else {
return APR_EACCES;
}
if (flag & APR_READCONTROL)
oflags |= READ_CONTROL;
}
if (flag & APR_FOPEN_XTHREAD) {
/* This win32 specific feature is required
* to allow multiple threads to work with the file.
*/
attributes |= FILE_FLAG_OVERLAPPED;
}
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wfname[APR_PATH_MAX];
if (flag & APR_FOPEN_SENDFILE_ENABLED) {
/* This feature is required to enable sendfile operations
* against the file on Win32. Also implies APR_FOPEN_XTHREAD.
*/
flag |= APR_FOPEN_XTHREAD;
attributes |= FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED;
}
if ((rv = utf8_to_unicode_path(wfname, sizeof(wfname)
/ sizeof(apr_wchar_t), fname)))
return rv;
handle = CreateFileW(wfname, oflags, sharemode,
NULL, createflags, attributes, 0);
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI {
handle = CreateFileA(fname, oflags, sharemode,
NULL, createflags, attributes, 0);
/* This feature is not supported on this platform. */
flag &= ~APR_FOPEN_SENDFILE_ENABLED;
}
#endif
if (handle == INVALID_HANDLE_VALUE) {
return apr_get_os_error();
}
(*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
(*new)->pool = pool;
(*new)->filehand = handle;
(*new)->fname = apr_pstrdup(pool, fname);
(*new)->flags = flag;
(*new)->timeout = -1;
(*new)->ungetchar = -1;
if (flag & APR_FOPEN_APPEND) {
(*new)->append = 1;
SetFilePointer((*new)->filehand, 0, NULL, FILE_END);
}
if (flag & APR_FOPEN_BUFFERED) {
(*new)->buffered = 1;
(*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
(*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
}
/* Need the mutex to handled buffered and O_APPEND style file i/o */
if ((*new)->buffered || (*new)->append) {
rv = apr_thread_mutex_create(&(*new)->mutex,
APR_THREAD_MUTEX_DEFAULT, pool);
if (rv) {
if (file_cleanup(*new) == APR_SUCCESS) {
apr_pool_cleanup_kill(pool, *new, file_cleanup);
}
return rv;
}
}
#if APR_HAS_UNICODE_FS
if ((apr_os_level >= APR_WIN_2000) && ((*new)->flags & APR_FOPEN_SPARSE)) {
if ((rv = make_sparse_file(*new)) != APR_SUCCESS)
/* The great mystery; do we close the file and return an error?
* Do we add a new APR_INCOMPLETE style error saying opened, but
* NOTSPARSE? For now let's simply mark the file as not-sparse.
*/
(*new)->flags &= ~APR_FOPEN_SPARSE;
}
else
#endif
/* This feature is not supported on this platform. */
(*new)->flags &= ~APR_FOPEN_SPARSE;
#if APR_FILES_AS_SOCKETS
/* Create a pollset with room for one descriptor. */
/* ### check return codes */
(void) apr_pollset_create(&(*new)->pollset, 1, pool, 0);
#endif
if (!(flag & APR_FOPEN_NOCLEANUP)) {
apr_pool_cleanup_register((*new)->pool, (void *)(*new), file_cleanup,
apr_pool_cleanup_null);
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
{
apr_status_t stat;
if ((stat = file_cleanup(file)) == APR_SUCCESS) {
apr_pool_cleanup_kill(file->pool, file, file_cleanup);
if (file->mutex) {
apr_thread_mutex_destroy(file->mutex);
}
return APR_SUCCESS;
}
return stat;
}
APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wpath[APR_PATH_MAX];
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wpath, sizeof(wpath)
/ sizeof(apr_wchar_t), path))) {
return rv;
}
if (DeleteFileW(wpath))
return APR_SUCCESS;
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
if (DeleteFile(path))
return APR_SUCCESS;
#endif
return apr_get_os_error();
}
APR_DECLARE(apr_status_t) apr_file_rename(const char *frompath,
const char *topath,
apr_pool_t *pool)
{
IF_WIN_OS_IS_UNICODE
{
#if APR_HAS_UNICODE_FS
apr_wchar_t wfrompath[APR_PATH_MAX], wtopath[APR_PATH_MAX];
apr_status_t rv;
if ((rv = utf8_to_unicode_path(wfrompath,
sizeof(wfrompath) / sizeof(apr_wchar_t),
frompath))) {
return rv;
}
if ((rv = utf8_to_unicode_path(wtopath,
sizeof(wtopath) / sizeof(apr_wchar_t),
topath))) {
return rv;
}
#ifndef _WIN32_WCE
if (MoveFileExW(wfrompath, wtopath, MOVEFILE_REPLACE_EXISTING |
MOVEFILE_COPY_ALLOWED))
#else
if (MoveFileW(wfrompath, wtopath))
#endif
return APR_SUCCESS;
#else
if (MoveFileEx(frompath, topath, MOVEFILE_REPLACE_EXISTING |
MOVEFILE_COPY_ALLOWED))
return APR_SUCCESS;
#endif
}
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
{
/* Windows 95 and 98 do not support MoveFileEx, so we'll use
* the old MoveFile function. However, MoveFile requires that
* the new file not already exist...so we have to delete that
* file if it does. Perhaps we should back up the to-be-deleted
* file in case something happens?
*/
HANDLE handle = INVALID_HANDLE_VALUE;
if ((handle = CreateFile(topath, GENERIC_WRITE, 0, 0,
OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE )
{
CloseHandle(handle);
if (!DeleteFile(topath))
return apr_get_os_error();
}
if (MoveFile(frompath, topath))
return APR_SUCCESS;
}
#endif
return apr_get_os_error();
}
APR_DECLARE(apr_status_t) apr_file_link(const char *from_path,
const char *to_path)
{
apr_status_t rv = APR_SUCCESS;
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
apr_wchar_t wfrom_path[APR_PATH_MAX];
apr_wchar_t wto_path[APR_PATH_MAX];
if ((rv = utf8_to_unicode_path(wfrom_path,
sizeof(wfrom_path) / sizeof(apr_wchar_t),
from_path)))
return rv;
if ((rv = utf8_to_unicode_path(wto_path,
sizeof(wto_path) / sizeof(apr_wchar_t),
to_path)))
return rv;
if (!CreateHardLinkW(wto_path, wfrom_path, NULL))
return apr_get_os_error();
}
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI {
if (!CreateHardLinkA(to_path, from_path, NULL))
return apr_get_os_error();
}
#endif
return rv;
}
APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
apr_file_t *file)
{
*thefile = file->filehand;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_os_file_put(apr_file_t **file,
apr_os_file_t *thefile,
apr_int32_t flags,
apr_pool_t *pool)
{
(*file) = apr_pcalloc(pool, sizeof(apr_file_t));
(*file)->pool = pool;
(*file)->filehand = *thefile;
(*file)->ungetchar = -1; /* no char avail */
(*file)->timeout = -1;
(*file)->flags = flags;
if (flags & APR_FOPEN_APPEND) {
(*file)->append = 1;
}
if (flags & APR_FOPEN_BUFFERED) {
(*file)->buffered = 1;
(*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
(*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
}
if ((*file)->append || (*file)->buffered) {
apr_status_t rv;
rv = apr_thread_mutex_create(&(*file)->mutex,
APR_THREAD_MUTEX_DEFAULT, pool);
if (rv) {
return rv;
}
}
#if APR_FILES_AS_SOCKETS
/* Create a pollset with room for one descriptor. */
/* ### check return codes */
(void) apr_pollset_create(&(*file)->pollset, 1, pool, 0);
#endif
/* Should we be testing if thefile is a handle to
* a PIPE and set up the mechanics appropriately?
*
* (*file)->pipe;
*/
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
{
if (fptr->eof_hit == 1) {
return APR_EOF;
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_open_flags_stderr(apr_file_t **thefile,
apr_int32_t flags,
apr_pool_t *pool)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
apr_os_file_t file_handle;
apr_set_os_error(APR_SUCCESS);
file_handle = GetStdHandle(STD_ERROR_HANDLE);
if (!file_handle)
file_handle = INVALID_HANDLE_VALUE;
return apr_os_file_put(thefile, &file_handle,
flags | APR_FOPEN_WRITE | APR_STDERR_FLAG, pool);
#endif
}
APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
apr_int32_t flags,
apr_pool_t *pool)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
apr_os_file_t file_handle;
apr_set_os_error(APR_SUCCESS);
file_handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (!file_handle)
file_handle = INVALID_HANDLE_VALUE;
return apr_os_file_put(thefile, &file_handle,
flags | APR_FOPEN_WRITE | APR_STDOUT_FLAG, pool);
#endif
}
APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
apr_int32_t flags,
apr_pool_t *pool)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
apr_os_file_t file_handle;
apr_set_os_error(APR_SUCCESS);
file_handle = GetStdHandle(STD_INPUT_HANDLE);
if (!file_handle)
file_handle = INVALID_HANDLE_VALUE;
return apr_os_file_put(thefile, &file_handle,
flags | APR_FOPEN_READ | APR_STDIN_FLAG, pool);
#endif
}
APR_DECLARE(apr_status_t) apr_file_open_stderr(apr_file_t **thefile, apr_pool_t *pool)
{
return apr_file_open_flags_stderr(thefile, 0, pool);
}
APR_DECLARE(apr_status_t) apr_file_open_stdout(apr_file_t **thefile, apr_pool_t *pool)
{
return apr_file_open_flags_stdout(thefile, 0, pool);
}
APR_DECLARE(apr_status_t) apr_file_open_stdin(apr_file_t **thefile, apr_pool_t *pool)
{
return apr_file_open_flags_stdin(thefile, 0, pool);
}
APR_POOL_IMPLEMENT_ACCESSOR(file);
APR_IMPLEMENT_INHERIT_SET(file, flags, pool, file_cleanup)
APR_IMPLEMENT_INHERIT_UNSET(file, flags, pool, file_cleanup)
+486
View File
@@ -0,0 +1,486 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_escape.h"
#if APR_HAVE_ERRNO_H
#include <errno.h>
#endif
#include <string.h>
#include <stdio.h>
#if APR_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if APR_HAVE_PROCESS_H
#include <process.h> /* for getpid() on Win32 */
#endif
#include "apr_arch_misc.h"
APR_DECLARE(apr_status_t) apr_file_pipe_timeout_set(apr_file_t *thepipe,
apr_interval_time_t timeout)
{
/* Always OK to unset timeouts */
if (timeout == -1) {
thepipe->timeout = timeout;
return APR_SUCCESS;
}
if (!thepipe->pipe) {
return APR_ENOTIMPL;
}
if (timeout && !(thepipe->pOverlapped)) {
/* Cannot be nonzero if a pipe was opened blocking
*/
return APR_EINVAL;
}
thepipe->timeout = timeout;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_pipe_timeout_get(apr_file_t *thepipe,
apr_interval_time_t *timeout)
{
/* Always OK to get the timeout (even if it's unset ... -1) */
*timeout = thepipe->timeout;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_pipe_create(apr_file_t **in,
apr_file_t **out,
apr_pool_t *p)
{
/* Unix creates full blocking pipes. */
return apr_file_pipe_create_pools(in, out, APR_FULL_BLOCK, p, p);
}
APR_DECLARE(apr_status_t) apr_file_pipe_create_ex(apr_file_t **in,
apr_file_t **out,
apr_int32_t blocking,
apr_pool_t *p)
{
return apr_file_pipe_create_pools(in, out, blocking, p, p);
}
APR_DECLARE(apr_status_t) apr_file_pipe_create_pools(apr_file_t **in,
apr_file_t **out,
apr_int32_t blocking,
apr_pool_t *pool_in,
apr_pool_t *pool_out)
{
#ifdef _WIN32_WCE
return APR_ENOTIMPL;
#else
SECURITY_ATTRIBUTES sa;
static unsigned long id = 0;
DWORD dwPipeMode;
DWORD dwOpenMode;
sa.nLength = sizeof(sa);
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
sa.bInheritHandle = FALSE;
#endif
#if APR_HAS_ANSI_FS
ELSE_WIN_OS_IS_ANSI
sa.bInheritHandle = TRUE;
#endif
sa.lpSecurityDescriptor = NULL;
(*in) = (apr_file_t *)apr_pcalloc(pool_in, sizeof(apr_file_t));
(*in)->pool = pool_in;
(*in)->fname = NULL;
(*in)->pipe = 1;
(*in)->timeout = -1;
(*in)->ungetchar = -1;
(*in)->eof_hit = 0;
(*in)->filePtr = 0;
(*in)->bufpos = 0;
(*in)->dataRead = 0;
(*in)->direction = 0;
(*in)->pOverlapped = NULL;
#if APR_FILES_AS_SOCKETS
(void) apr_pollset_create(&(*in)->pollset, 1, p, 0);
#endif
(*out) = (apr_file_t *)apr_pcalloc(pool_out, sizeof(apr_file_t));
(*out)->pool = pool_out;
(*out)->fname = NULL;
(*out)->pipe = 1;
(*out)->timeout = -1;
(*out)->ungetchar = -1;
(*out)->eof_hit = 0;
(*out)->filePtr = 0;
(*out)->bufpos = 0;
(*out)->dataRead = 0;
(*out)->direction = 0;
(*out)->pOverlapped = NULL;
#if APR_FILES_AS_SOCKETS
(void) apr_pollset_create(&(*out)->pollset, 1, p, 0);
#endif
if (apr_os_level >= APR_WIN_NT) {
char rand[8];
int pid = getpid();
#define FMT_PIPE_NAME "\\\\.\\pipe\\apr-pipe-%x.%lx."
/* ^ ^ ^
* pid | |
* | |
* id |
* |
* hex-escaped rand[8] (16 bytes)
*/
char name[sizeof FMT_PIPE_NAME + 2 * sizeof(pid)
+ 2 * sizeof(id)
+ 2 * sizeof(rand)];
apr_size_t pos;
/* Create the read end of the pipe */
dwOpenMode = PIPE_ACCESS_INBOUND;
#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
#endif
if (blocking == APR_WRITE_BLOCK /* READ_NONBLOCK */
|| blocking == APR_FULL_NONBLOCK) {
dwOpenMode |= FILE_FLAG_OVERLAPPED;
(*in)->pOverlapped =
(OVERLAPPED*) apr_pcalloc((*in)->pool, sizeof(OVERLAPPED));
(*in)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
(*in)->timeout = 0;
}
dwPipeMode = 0;
apr_generate_random_bytes(rand, sizeof rand);
pos = apr_snprintf(name, sizeof name, FMT_PIPE_NAME, pid, id++);
apr_escape_hex(name + pos, rand, sizeof rand, 0, NULL);
(*in)->filehand = CreateNamedPipe(name,
dwOpenMode,
dwPipeMode,
1, /* nMaxInstances, */
0, /* nOutBufferSize, */
65536, /* nInBufferSize, */
1, /* nDefaultTimeOut, */
&sa);
if ((*in)->filehand == INVALID_HANDLE_VALUE) {
apr_status_t rv = apr_get_os_error();
file_cleanup(*in);
return rv;
}
/* Create the write end of the pipe */
dwOpenMode = FILE_ATTRIBUTE_NORMAL;
if (blocking == APR_READ_BLOCK /* WRITE_NONBLOCK */
|| blocking == APR_FULL_NONBLOCK) {
dwOpenMode |= FILE_FLAG_OVERLAPPED;
(*out)->pOverlapped =
(OVERLAPPED*) apr_pcalloc((*out)->pool, sizeof(OVERLAPPED));
(*out)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
(*out)->timeout = 0;
}
(*out)->filehand = CreateFile(name,
GENERIC_WRITE, /* access mode */
0, /* share mode */
&sa, /* Security attributes */
OPEN_EXISTING, /* dwCreationDisposition */
dwOpenMode, /* Pipe attributes */
NULL); /* handle to template file */
if ((*out)->filehand == INVALID_HANDLE_VALUE) {
apr_status_t rv = apr_get_os_error();
file_cleanup(*out);
file_cleanup(*in);
return rv;
}
}
else {
/* Pipes on Win9* are blocking. Live with it. */
if (!CreatePipe(&(*in)->filehand, &(*out)->filehand, &sa, 65536)) {
return apr_get_os_error();
}
}
apr_pool_cleanup_register((*in)->pool, (void *)(*in), file_cleanup,
apr_pool_cleanup_null);
apr_pool_cleanup_register((*out)->pool, (void *)(*out), file_cleanup,
apr_pool_cleanup_null);
return APR_SUCCESS;
#endif /* _WIN32_WCE */
}
APR_DECLARE(apr_status_t) apr_file_namedpipe_create(const char *filename,
apr_fileperms_t perm,
apr_pool_t *pool)
{
/* Not yet implemented, interface not suitable.
* Win32 requires the named pipe to be *opened* at the time it's
* created, and to do so, blocking or non blocking must be elected.
*/
return APR_ENOTIMPL;
}
/* XXX: Problem; we need to choose between blocking and nonblocking based
* on how *thefile was opened, and we don't have that information :-/
* Hack; assume a blocking socket, since the most common use for the fn
* would be to handle stdio-style or blocking pipes. Win32 doesn't have
* select() blocking for pipes anyways :(
*/
APR_DECLARE(apr_status_t) apr_os_pipe_put_ex(apr_file_t **file,
apr_os_file_t *thefile,
int register_cleanup,
apr_pool_t *pool)
{
(*file) = apr_pcalloc(pool, sizeof(apr_file_t));
(*file)->pool = pool;
(*file)->pipe = 1;
(*file)->timeout = -1;
(*file)->ungetchar = -1;
(*file)->filehand = *thefile;
#if APR_FILES_AS_SOCKETS
(void) apr_pollset_create(&(*file)->pollset, 1, pool, 0);
#endif
if (register_cleanup) {
apr_pool_cleanup_register(pool, *file, file_cleanup,
apr_pool_cleanup_null);
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_os_pipe_put(apr_file_t **file,
apr_os_file_t *thefile,
apr_pool_t *pool)
{
return apr_os_pipe_put_ex(file, thefile, 0, pool);
}
static apr_status_t create_socket_pipe(SOCKET *rd, SOCKET *wr)
{
static int id = 0;
FD_SET rs;
SOCKET ls;
struct timeval socktm;
struct sockaddr_in pa;
struct sockaddr_in la;
struct sockaddr_in ca;
int nrd;
apr_status_t rv = APR_SUCCESS;
int ll = sizeof(la);
int lc = sizeof(ca);
unsigned long bm = 1;
int uid[2];
int iid[2];
*rd = INVALID_SOCKET;
*wr = INVALID_SOCKET;
/* Create the unique socket identifier
* so that we know the connection originated
* from us.
*/
uid[0] = getpid();
uid[1] = id++;
if ((ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
return apr_get_netos_error();
}
pa.sin_family = AF_INET;
pa.sin_port = 0;
pa.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(ls, (SOCKADDR *)&pa, sizeof(pa)) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (getsockname(ls, (SOCKADDR *)&la, &ll) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (listen(ls, 1) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if ((*wr = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
rv = apr_get_netos_error();
goto cleanup;
}
if (connect(*wr, (SOCKADDR *)&la, sizeof(la)) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
if (send(*wr, (char *)uid, sizeof(uid), 0) != sizeof(uid)) {
if ((rv = apr_get_netos_error()) == 0) {
rv = APR_EINVAL;
}
goto cleanup;
}
if (ioctlsocket(ls, FIONBIO, &bm) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
for (;;) {
int ns;
int nc = 0;
/* Listening socket is nonblocking by now.
* The accept should create the socket
* immediatelly because we are connected already.
* However on buys systems this can take a while
* until winsock gets a chance to handle the events.
*/
FD_ZERO(&rs);
FD_SET(ls, &rs);
socktm.tv_sec = 1;
socktm.tv_usec = 0;
if ((ns = select(0, &rs, NULL, NULL, &socktm)) == SOCKET_ERROR) {
/* Accept still not signaled */
Sleep(100);
continue;
}
if (ns == 0) {
/* No connections in the last second */
continue;
}
if ((*rd = accept(ls, (SOCKADDR *)&ca, &lc)) == INVALID_SOCKET) {
rv = apr_get_netos_error();
goto cleanup;
}
/* Verify the connection by reading the send identification.
*/
do {
if (nc++)
Sleep(1);
nrd = recv(*rd, (char *)iid, sizeof(iid), 0);
rv = nrd == SOCKET_ERROR ? apr_get_netos_error() : APR_SUCCESS;
} while (APR_STATUS_IS_EAGAIN(rv));
if (nrd == sizeof(iid)) {
if (memcmp(uid, iid, sizeof(uid)) == 0) {
/* Wow, we recived what we send.
* Put read side of the pipe to the blocking
* mode and return.
*/
bm = 0;
if (ioctlsocket(*rd, FIONBIO, &bm) == SOCKET_ERROR) {
rv = apr_get_netos_error();
goto cleanup;
}
break;
}
}
else if (nrd == SOCKET_ERROR) {
goto cleanup;
}
closesocket(*rd);
}
/* We don't need the listening socket any more */
closesocket(ls);
return 0;
cleanup:
/* Don't leak resources */
if (*rd != INVALID_SOCKET)
closesocket(*rd);
if (*wr != INVALID_SOCKET)
closesocket(*wr);
*rd = INVALID_SOCKET;
*wr = INVALID_SOCKET;
closesocket(ls);
return rv;
}
static apr_status_t socket_pipe_cleanup(void *thefile)
{
apr_file_t *file = thefile;
if (file->filehand != INVALID_HANDLE_VALUE) {
shutdown((SOCKET)file->filehand, SD_BOTH);
closesocket((SOCKET)file->filehand);
file->filehand = INVALID_HANDLE_VALUE;
}
return APR_SUCCESS;
}
apr_status_t apr_file_socket_pipe_create(apr_file_t **in,
apr_file_t **out,
apr_pool_t *p)
{
apr_status_t rv;
SOCKET rd;
SOCKET wr;
if ((rv = create_socket_pipe(&rd, &wr)) != APR_SUCCESS) {
return rv;
}
(*in) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t));
(*in)->pool = p;
(*in)->fname = NULL;
(*in)->pipe = 1;
(*in)->timeout = -1;
(*in)->ungetchar = -1;
(*in)->eof_hit = 0;
(*in)->filePtr = 0;
(*in)->bufpos = 0;
(*in)->dataRead = 0;
(*in)->direction = 0;
(*in)->pOverlapped = (OVERLAPPED*)apr_pcalloc(p, sizeof(OVERLAPPED));
(*in)->filehand = (HANDLE)rd;
(*out) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t));
(*out)->pool = p;
(*out)->fname = NULL;
(*out)->pipe = 1;
(*out)->timeout = -1;
(*out)->ungetchar = -1;
(*out)->eof_hit = 0;
(*out)->filePtr = 0;
(*out)->bufpos = 0;
(*out)->dataRead = 0;
(*out)->direction = 0;
(*out)->pOverlapped = (OVERLAPPED*)apr_pcalloc(p, sizeof(OVERLAPPED));
(*out)->filehand = (HANDLE)wr;
apr_pool_cleanup_register(p, (void *)(*in), socket_pipe_cleanup,
apr_pool_cleanup_null);
apr_pool_cleanup_register(p, (void *)(*out), socket_pipe_cleanup,
apr_pool_cleanup_null);
return rv;
}
apr_status_t apr_file_socket_pipe_close(apr_file_t *file)
{
apr_status_t stat;
if (!file->pipe)
return apr_file_close(file);
if ((stat = socket_pipe_cleanup(file)) == APR_SUCCESS) {
apr_pool_cleanup_kill(file->pool, file, socket_pipe_cleanup);
if (file->mutex) {
apr_thread_mutex_destroy(file->mutex);
}
return APR_SUCCESS;
}
return stat;
}
+592
View File
@@ -0,0 +1,592 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include "apr_general.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_errno.h"
#include <malloc.h>
#include "apr_arch_atime.h"
#include "apr_arch_misc.h"
/*
* read_with_timeout()
* Uses async i/o to emulate unix non-blocking i/o with timeouts.
*/
static apr_status_t read_with_timeout(apr_file_t *file, void *buf, apr_size_t len_in, apr_size_t *nbytes)
{
apr_status_t rv;
DWORD res;
DWORD len = (DWORD)len_in;
DWORD bytesread = 0;
/* Handle the zero timeout non-blocking case */
if (file->timeout == 0) {
/* Peek at the pipe. If there is no data available, return APR_EAGAIN.
* If data is available, go ahead and read it.
*/
if (file->pipe) {
DWORD bytes;
if (!PeekNamedPipe(file->filehand, NULL, 0, NULL, &bytes, NULL)) {
rv = apr_get_os_error();
if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
rv = APR_EOF;
}
*nbytes = 0;
return rv;
}
else {
if (bytes == 0) {
*nbytes = 0;
return APR_EAGAIN;
}
if (len > bytes) {
len = bytes;
}
}
}
else {
/* ToDo: Handle zero timeout non-blocking file i/o
* This is not needed until an APR application needs to
* timeout file i/o (which means setting file i/o non-blocking)
*/
}
}
if (file->pOverlapped && !file->pipe) {
file->pOverlapped->Offset = (DWORD)file->filePtr;
file->pOverlapped->OffsetHigh = (DWORD)(file->filePtr >> 32);
}
if (ReadFile(file->filehand, buf, len,
&bytesread, file->pOverlapped)) {
rv = APR_SUCCESS;
}
else {
rv = apr_get_os_error();
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
/* Wait for the pending i/o, timeout converted from us to ms
* Note that we loop if someone gives up the event, since
* folks suggest that WAIT_ABANDONED isn't actually a result
* but an alert that ownership of the event has passed from
* one owner to a new proc/thread.
*/
do {
res = WaitForSingleObject(file->pOverlapped->hEvent,
(file->timeout > 0)
? (DWORD)(file->timeout/1000)
: ((file->timeout == -1)
? INFINITE : 0));
} while (res == WAIT_ABANDONED);
/* There is one case that represents entirely
* successful operations, otherwise we will cancel
* the operation in progress.
*/
if (res != WAIT_OBJECT_0) {
CancelIo(file->filehand);
}
/* Ignore any failures above. Attempt to complete
* the overlapped operation and use only _its_ result.
* For example, CancelIo or WaitForSingleObject can
* fail if the handle is closed, yet the read may have
* completed before we attempted to CancelIo...
*/
if (GetOverlappedResult(file->filehand, file->pOverlapped,
&bytesread, TRUE)) {
rv = APR_SUCCESS;
}
else {
rv = apr_get_os_error();
if (((rv == APR_FROM_OS_ERROR(ERROR_IO_INCOMPLETE))
|| (rv == APR_FROM_OS_ERROR(ERROR_OPERATION_ABORTED)))
&& (res == WAIT_TIMEOUT))
rv = APR_TIMEUP;
}
}
if (rv == APR_FROM_OS_ERROR(ERROR_BROKEN_PIPE)) {
/* Assume ERROR_BROKEN_PIPE signals an EOF reading from a pipe */
rv = APR_EOF;
} else if (rv == APR_FROM_OS_ERROR(ERROR_HANDLE_EOF)) {
/* Did we hit EOF reading from the handle? */
rv = APR_EOF;
}
}
/* OK and 0 bytes read ==> end of file */
if (rv == APR_SUCCESS && bytesread == 0)
rv = APR_EOF;
if (rv == APR_SUCCESS && file->pOverlapped && !file->pipe) {
file->filePtr += bytesread;
}
*nbytes = bytesread;
return rv;
}
APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *len)
{
apr_status_t rv;
DWORD bytes_read = 0;
if (*len <= 0) {
*len = 0;
return APR_SUCCESS;
}
/* If the file is open for xthread support, allocate and
* initialize the overlapped and io completion event (hEvent).
* Threads should NOT share an apr_file_t or its hEvent.
*/
if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
sizeof(OVERLAPPED));
thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!thefile->pOverlapped->hEvent) {
rv = apr_get_os_error();
return rv;
}
}
/* Handle the ungetchar if there is one */
if (thefile->ungetchar != -1) {
bytes_read = 1;
*(char *)buf = (char)thefile->ungetchar;
buf = (char *)buf + 1;
(*len)--;
thefile->ungetchar = -1;
if (*len == 0) {
*len = bytes_read;
return APR_SUCCESS;
}
}
if (thefile->buffered) {
char *pos = (char *)buf;
apr_size_t blocksize;
apr_size_t size = *len;
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(thefile->mutex);
}
if (thefile->direction == 1) {
rv = apr_file_flush(thefile);
if (rv != APR_SUCCESS) {
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(thefile->mutex);
}
return rv;
}
thefile->bufpos = 0;
thefile->direction = 0;
thefile->dataRead = 0;
}
rv = 0;
while (rv == 0 && size > 0) {
if (thefile->bufpos >= thefile->dataRead) {
apr_size_t read;
rv = read_with_timeout(thefile, thefile->buffer,
thefile->bufsize, &read);
if (read == 0) {
if (rv == APR_EOF)
thefile->eof_hit = TRUE;
break;
}
else {
thefile->dataRead = read;
thefile->filePtr += thefile->dataRead;
thefile->bufpos = 0;
}
}
blocksize = size > thefile->dataRead - thefile->bufpos ? thefile->dataRead - thefile->bufpos : size;
memcpy(pos, thefile->buffer + thefile->bufpos, blocksize);
thefile->bufpos += blocksize;
pos += blocksize;
size -= blocksize;
}
*len = pos - (char *)buf;
if (*len) {
rv = APR_SUCCESS;
}
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(thefile->mutex);
}
} else {
/* Unbuffered i/o */
apr_size_t nbytes;
rv = read_with_timeout(thefile, buf, *len, &nbytes);
if (rv == APR_EOF)
thefile->eof_hit = TRUE;
*len = nbytes;
}
return rv;
}
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
{
apr_status_t rv;
DWORD bwrote;
/* If the file is open for xthread support, allocate and
* initialize the overlapped and io completion event (hEvent).
* Threads should NOT share an apr_file_t or its hEvent.
*/
if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
sizeof(OVERLAPPED));
thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!thefile->pOverlapped->hEvent) {
rv = apr_get_os_error();
return rv;
}
}
if (thefile->buffered) {
char *pos = (char *)buf;
apr_size_t blocksize;
apr_size_t size = *nbytes;
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(thefile->mutex);
}
if (thefile->direction == 0) {
/* Position file pointer for writing at the offset we are logically reading from */
apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
DWORD offlo = (DWORD)offset;
LONG offhi = (LONG)(offset >> 32);
if (offset != thefile->filePtr)
SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
thefile->bufpos = thefile->dataRead = 0;
thefile->direction = 1;
}
rv = 0;
while (rv == 0 && size > 0) {
if (thefile->bufpos == thefile->bufsize) /* write buffer is full */
rv = apr_file_flush(thefile);
blocksize = size > thefile->bufsize - thefile->bufpos ?
thefile->bufsize - thefile->bufpos : size;
memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
thefile->bufpos += blocksize;
pos += blocksize;
size -= blocksize;
}
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(thefile->mutex);
}
return rv;
} else {
if (!thefile->pipe) {
apr_off_t offset = 0;
apr_status_t rc;
if (thefile->append) {
/* apr_file_lock will mutex the file across processes.
* The call to apr_thread_mutex_lock is added to avoid
* a race condition between LockFile and WriteFile
* that occasionally leads to deadlocked threads.
*/
apr_thread_mutex_lock(thefile->mutex);
rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
if (rc != APR_SUCCESS) {
apr_thread_mutex_unlock(thefile->mutex);
return rc;
}
rc = apr_file_seek(thefile, APR_END, &offset);
if (rc != APR_SUCCESS) {
apr_thread_mutex_unlock(thefile->mutex);
return rc;
}
}
if (thefile->pOverlapped) {
thefile->pOverlapped->Offset = (DWORD)thefile->filePtr;
thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
}
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
thefile->pOverlapped);
if (thefile->append) {
apr_file_unlock(thefile);
apr_thread_mutex_unlock(thefile->mutex);
}
}
else {
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
thefile->pOverlapped);
}
if (rv) {
*nbytes = bwrote;
rv = APR_SUCCESS;
}
else {
(*nbytes) = 0;
rv = apr_get_os_error();
/* XXX: This must be corrected, per the apr_file_read logic!!! */
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
DWORD timeout_ms;
if (thefile->timeout == 0) {
timeout_ms = 0;
}
else if (thefile->timeout < 0) {
timeout_ms = INFINITE;
}
else {
timeout_ms = (DWORD)(thefile->timeout / 1000);
}
rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
switch (rv) {
case WAIT_OBJECT_0:
GetOverlappedResult(thefile->filehand, thefile->pOverlapped,
&bwrote, TRUE);
*nbytes = bwrote;
rv = APR_SUCCESS;
break;
case WAIT_TIMEOUT:
rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP;
break;
case WAIT_FAILED:
rv = apr_get_os_error();
break;
default:
break;
}
if (rv != APR_SUCCESS) {
if (apr_os_level >= APR_WIN_98)
CancelIo(thefile->filehand);
}
}
}
if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
thefile->filePtr += *nbytes;
}
}
return rv;
}
/* ToDo: Write for it anyway and test the oslevel!
* Too bad WriteFileGather() is not supported on 95&98 (or NT prior to SP2)
*/
APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile,
const struct iovec *vec,
apr_size_t nvec,
apr_size_t *nbytes)
{
apr_status_t rv = APR_SUCCESS;
apr_size_t i;
apr_size_t bwrote = 0;
char *buf;
*nbytes = 0;
for (i = 0; i < nvec; i++) {
buf = vec[i].iov_base;
bwrote = vec[i].iov_len;
rv = apr_file_write(thefile, buf, &bwrote);
*nbytes += bwrote;
if (rv != APR_SUCCESS) {
break;
}
}
return rv;
}
APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
{
apr_size_t len = 1;
return apr_file_write(thefile, &ch, &len);
}
APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
{
thefile->ungetchar = (unsigned char) ch;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
{
apr_status_t rc;
apr_size_t bread;
bread = 1;
rc = apr_file_read(thefile, ch, &bread);
if (rc) {
return rc;
}
if (bread == 0) {
thefile->eof_hit = TRUE;
return APR_EOF;
}
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
{
apr_size_t len = strlen(str);
return apr_file_write(thefile, str, &len);
}
APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
{
apr_size_t readlen;
apr_status_t rv = APR_SUCCESS;
int i;
for (i = 0; i < len-1; i++) {
readlen = 1;
rv = apr_file_read(thefile, str+i, &readlen);
if (rv != APR_SUCCESS && rv != APR_EOF)
return rv;
if (readlen == 0) {
/* If we have bytes, defer APR_EOF to the next call */
if (i > 0)
rv = APR_SUCCESS;
break;
}
if (str[i] == '\n') {
i++; /* don't clobber this char below */
break;
}
}
str[i] = 0;
return rv;
}
APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
{
if (thefile->buffered) {
DWORD numbytes, written = 0;
apr_status_t rc = 0;
char *buffer;
apr_size_t bytesleft;
if (thefile->direction == 1 && thefile->bufpos) {
buffer = thefile->buffer;
bytesleft = thefile->bufpos;
do {
if (bytesleft > APR_DWORD_MAX) {
numbytes = APR_DWORD_MAX;
}
else {
numbytes = (DWORD)bytesleft;
}
if (!WriteFile(thefile->filehand, buffer, numbytes, &written, NULL)) {
rc = apr_get_os_error();
thefile->filePtr += written;
break;
}
thefile->filePtr += written;
bytesleft -= written;
buffer += written;
} while (bytesleft > 0);
if (rc == 0)
thefile->bufpos = 0;
}
return rc;
}
/* There isn't anything to do if we aren't buffering the output
* so just return success.
*/
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile){
apr_status_t rv;
rv = apr_file_flush(thefile);
if (rv != APR_SUCCESS) {
return rv;
}
if (!FlushFileBuffers(thefile->filehand)) {
rv = apr_get_os_error();
}
return rv;
}
APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile){
return apr_file_sync(thefile);
}
struct apr_file_printf_data {
apr_vformatter_buff_t vbuff;
apr_file_t *fptr;
char *buf;
};
static int file_printf_flush(apr_vformatter_buff_t *buff)
{
struct apr_file_printf_data *data = (struct apr_file_printf_data *)buff;
if (apr_file_write_full(data->fptr, data->buf,
data->vbuff.curpos - data->buf, NULL)) {
return -1;
}
data->vbuff.curpos = data->buf;
return 0;
}
APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
const char *format, ...)
{
struct apr_file_printf_data data;
va_list ap;
int count;
data.buf = malloc(HUGE_STRING_LEN);
if (data.buf == NULL) {
return 0;
}
data.vbuff.curpos = data.buf;
data.vbuff.endpos = data.buf + HUGE_STRING_LEN;
data.fptr = fptr;
va_start(ap, format);
count = apr_vformatter(file_printf_flush,
(apr_vformatter_buff_t *)&data, format, ap);
/* apr_vformatter does not call flush for the last bits */
if (count >= 0) file_printf_flush((apr_vformatter_buff_t *)&data);
va_end(ap);
free(data.buf);
return count;
}
+203
View File
@@ -0,0 +1,203 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr_arch_file_io.h"
#include "apr_file_io.h"
#include <errno.h>
#include <string.h>
static apr_status_t setptr(apr_file_t *thefile, apr_off_t pos )
{
apr_off_t newbufpos;
apr_status_t rv;
DWORD rc;
if (thefile->direction == 1) {
/* XXX: flush here is not mutex protected */
rv = apr_file_flush(thefile);
if (rv != APR_SUCCESS)
return rv;
thefile->bufpos = thefile->dataRead = 0;
thefile->direction = 0;
}
/* We may be truncating to size here.
* XXX: testing an 'unsigned' as >= 0 below indicates a bug
*/
newbufpos = pos - (thefile->filePtr - thefile->dataRead);
if (newbufpos >= 0 && newbufpos <= (apr_off_t)thefile->dataRead) {
thefile->bufpos = (apr_size_t)newbufpos;
rv = APR_SUCCESS;
} else {
DWORD offlo = (DWORD)pos;
LONG offhi = (LONG)(pos >> 32);
rc = SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
if (rc == (DWORD)-1)
/* A legal value, perhaps? MSDN implies prior SetLastError isn't
* needed, googling for SetLastError SetFilePointer seems
* to confirm this. INVALID_SET_FILE_POINTER is too recently
* added for us to rely on it as a constant.
*/
rv = apr_get_os_error();
else
rv = APR_SUCCESS;
if (rv == APR_SUCCESS) {
rv = APR_SUCCESS;
thefile->eof_hit = 0;
thefile->bufpos = thefile->dataRead = 0;
thefile->filePtr = pos;
}
}
return rv;
}
APR_DECLARE(apr_status_t) apr_file_seek(apr_file_t *thefile, apr_seek_where_t where, apr_off_t *offset)
{
apr_finfo_t finfo;
apr_status_t rc = APR_SUCCESS;
thefile->eof_hit = 0;
if (thefile->buffered) {
switch (where) {
case APR_SET:
rc = setptr(thefile, *offset);
break;
case APR_CUR:
rc = setptr(thefile, thefile->filePtr - thefile->dataRead
+ thefile->bufpos + *offset);
break;
case APR_END:
rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
if (rc == APR_SUCCESS)
rc = setptr(thefile, finfo.size + *offset);
break;
default:
return APR_EINVAL;
}
*offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
return rc;
}
/* A file opened with APR_FOPEN_XTHREAD has been opened for overlapped i/o.
* APR must explicitly track the file pointer in this case.
*/
else if (thefile->pOverlapped || thefile->flags & APR_FOPEN_XTHREAD) {
switch(where) {
case APR_SET:
thefile->filePtr = *offset;
break;
case APR_CUR:
thefile->filePtr += *offset;
break;
case APR_END:
rc = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
if (rc == APR_SUCCESS && finfo.size + *offset >= 0)
thefile->filePtr = finfo.size + *offset;
break;
default:
return APR_EINVAL;
}
*offset = thefile->filePtr;
return rc;
}
else {
DWORD howmove;
DWORD offlo = (DWORD)*offset;
DWORD offhi = (DWORD)(*offset >> 32);
switch(where) {
case APR_SET:
howmove = FILE_BEGIN; break;
case APR_CUR:
howmove = FILE_CURRENT; break;
case APR_END:
howmove = FILE_END; break;
default:
return APR_EINVAL;
}
offlo = SetFilePointer(thefile->filehand, (LONG)offlo,
(LONG*)&offhi, howmove);
if (offlo == 0xFFFFFFFF)
rc = apr_get_os_error();
else
rc = APR_SUCCESS;
/* Since we can land at 0xffffffff we will measure our APR_SUCCESS */
if (rc == APR_SUCCESS)
*offset = ((apr_off_t)offhi << 32) | offlo;
return rc;
}
}
APR_DECLARE(apr_status_t) apr_file_trunc(apr_file_t *thefile, apr_off_t offset)
{
apr_status_t rv;
DWORD offlo = (DWORD)offset;
LONG offhi = (LONG)(offset >> 32);
DWORD rc;
if (thefile->buffered) {
if (thefile->direction == 1) {
/* Figure out what needs to be flushed. Don't flush the part
* of the write buffer that will get truncated anyway.
*/
if (offset < thefile->filePtr) {
thefile->bufpos = 0;
}
else if (offset < thefile->filePtr + (apr_off_t)thefile->bufpos) {
thefile->bufpos = offset - thefile->filePtr;
}
if (thefile->bufpos != 0) {
rv = apr_file_flush(thefile);
if (rv != APR_SUCCESS)
return rv;
}
}
else if (thefile->direction == 0) {
/* Discard the read buffer, as we are about to reposition
* ourselves to the end of file.
*/
thefile->bufpos = 0;
thefile->dataRead = 0;
}
}
rc = SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
if (rc == 0xFFFFFFFF)
if ((rv = apr_get_os_error()) != APR_SUCCESS)
return rv;
thefile->filePtr = offset;
/* Don't report EOF until the next read. */
thefile->eof_hit = 0;
if (!SetEndOfFile(thefile->filehand))
return apr_get_os_error();
return APR_SUCCESS;
}