summaryrefslogtreecommitdiffstats
path: root/file_io
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
commit30d479c28c831a0d4f1fdb54a9e346b0fc176be1 (patch)
treeaa35d7414ce9f1326abf6f723f6dfa5b0aa08b1d /file_io
parentInitial commit. (diff)
downloadapr-upstream/1.7.2.tar.xz
apr-upstream/1.7.2.zip
Adding upstream version 1.7.2.upstream/1.7.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'file_io')
-rw-r--r--file_io/netware/filepath.c4
-rw-r--r--file_io/netware/filestat.c417
-rw-r--r--file_io/netware/filesys.c106
-rw-r--r--file_io/netware/flock.c39
-rw-r--r--file_io/netware/mktemp.c64
-rw-r--r--file_io/netware/pipe.c239
-rw-r--r--file_io/os2/buffer.c59
-rw-r--r--file_io/os2/copy.c1
-rw-r--r--file_io/os2/dir.c167
-rw-r--r--file_io/os2/dir_make_recurse.c90
-rw-r--r--file_io/os2/fileacc.c18
-rw-r--r--file_io/os2/filedup.c124
-rw-r--r--file_io/os2/filepath.c16
-rw-r--r--file_io/os2/filepath_util.c1
-rw-r--r--file_io/os2/filestat.c241
-rw-r--r--file_io/os2/filesys.c148
-rw-r--r--file_io/os2/flock.c37
-rw-r--r--file_io/os2/fullrw.c1
-rw-r--r--file_io/os2/maperrorcode.c95
-rw-r--r--file_io/os2/mktemp.c1
-rw-r--r--file_io/os2/open.c304
-rw-r--r--file_io/os2/pipe.c236
-rw-r--r--file_io/os2/readwrite.c388
-rw-r--r--file_io/os2/seek.c120
-rw-r--r--file_io/os2/tempdir.c1
-rw-r--r--file_io/unix/buffer.c60
-rw-r--r--file_io/unix/copy.c119
-rw-r--r--file_io/unix/dir.c373
-rw-r--r--file_io/unix/fileacc.c119
-rw-r--r--file_io/unix/filedup.c181
-rw-r--r--file_io/unix/filepath.c314
-rw-r--r--file_io/unix/filepath_util.c111
-rw-r--r--file_io/unix/filestat.c339
-rw-r--r--file_io/unix/flock.c120
-rw-r--r--file_io/unix/fullrw.c111
-rw-r--r--file_io/unix/mktemp.c226
-rw-r--r--file_io/unix/open.c417
-rw-r--r--file_io/unix/pipe.c292
-rw-r--r--file_io/unix/readwrite.c538
-rw-r--r--file_io/unix/seek.c136
-rw-r--r--file_io/unix/tempdir.c129
-rw-r--r--file_io/win32/buffer.c65
-rw-r--r--file_io/win32/dir.c408
-rw-r--r--file_io/win32/filedup.c228
-rw-r--r--file_io/win32/filepath.c1005
-rw-r--r--file_io/win32/filestat.c899
-rw-r--r--file_io/win32/filesys.c229
-rw-r--r--file_io/win32/flock.c86
-rw-r--r--file_io/win32/open.c755
-rw-r--r--file_io/win32/pipe.c486
-rw-r--r--file_io/win32/readwrite.c592
-rw-r--r--file_io/win32/seek.c203
52 files changed, 11458 insertions, 0 deletions
diff --git a/file_io/netware/filepath.c b/file_io/netware/filepath.c
new file mode 100644
index 0000000..e4bb3f3
--- /dev/null
+++ b/file_io/netware/filepath.c
@@ -0,0 +1,4 @@
+/* NetWare & Win32 have much in common with regards to file names (both are
+ * DOSish) so it makes sense to share some code
+ */
+#include "../win32/filepath.c"
diff --git a/file_io/netware/filestat.c b/file_io/netware/filestat.c
new file mode 100644
index 0000000..6627951
--- /dev/null
+++ b/file_io/netware/filestat.c
@@ -0,0 +1,417 @@
+/* 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 "fsio.h"
+#include "nks/dirio.h"
+#include "apr_file_io.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_errno.h"
+#include "apr_hash.h"
+#include "apr_thread_rwlock.h"
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+
+#define APR_HAS_PSA
+
+static apr_filetype_e filetype_from_mode(mode_t mode)
+{
+ apr_filetype_e type = APR_NOFILE;
+
+ if (S_ISREG(mode))
+ type = APR_REG;
+ else if (S_ISDIR(mode))
+ type = APR_DIR;
+ else if (S_ISCHR(mode))
+ type = APR_CHR;
+ else if (S_ISBLK(mode))
+ type = APR_BLK;
+ else if (S_ISFIFO(mode))
+ type = APR_PIPE;
+ else if (S_ISLNK(mode))
+ type = APR_LNK;
+ else if (S_ISSOCK(mode))
+ type = APR_SOCK;
+ else
+ type = APR_UNKFILE;
+ return type;
+}
+
+static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
+ apr_int32_t wanted)
+{
+ finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
+ | APR_FINFO_OWNER | APR_FINFO_PROT;
+
+ finfo->protection = apr_unix_mode2perms(info->st_mode);
+ finfo->filetype = filetype_from_mode(info->st_mode);
+ finfo->user = info->st_uid;
+ finfo->group = info->st_gid;
+ finfo->size = info->st_size;
+ finfo->inode = info->st_ino;
+ finfo->device = info->st_dev;
+ finfo->nlink = info->st_nlink;
+
+ apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
+ apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
+ apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);
+
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+#ifdef DEV_BSIZE
+ finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
+#else
+ finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
+#endif
+ finfo->valid |= APR_FINFO_CSIZE;
+#endif
+}
+
+apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted,
+ apr_file_t *thefile)
+{
+ struct_stat info;
+
+ if (thefile->buffered) {
+ apr_status_t rv = apr_file_flush_locked(thefile);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (fstat(thefile->filedes, &info) == 0) {
+ finfo->pool = thefile->pool;
+ finfo->fname = thefile->fname;
+ fill_out_finfo(finfo, &info, wanted);
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
+ apr_int32_t wanted,
+ apr_file_t *thefile)
+{
+ struct stat info;
+
+ if (thefile->buffered) {
+ /* XXX: flush here is not mutex protected */
+ apr_status_t rv = apr_file_flush(thefile);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (fstat(thefile->filedes, &info) == 0) {
+ finfo->pool = thefile->pool;
+ finfo->fname = thefile->fname;
+ fill_out_finfo(finfo, &info, wanted);
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
+ apr_fileperms_t perms)
+{
+ mode_t mode = apr_unix_perms2mode(perms);
+
+ if (chmod(fname, mode) == -1)
+ return errno;
+ 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)
+{
+ apr_status_t status;
+ apr_finfo_t finfo;
+
+ /* Don't do anything if we can't handle the requested attributes */
+ if (!(attr_mask & (APR_FILE_ATTR_READONLY
+ | APR_FILE_ATTR_EXECUTABLE)))
+ return APR_SUCCESS;
+
+ status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool);
+ if (status)
+ return status;
+
+ /* ### TODO: should added bits be umask'd? */
+ if (attr_mask & APR_FILE_ATTR_READONLY)
+ {
+ if (attributes & APR_FILE_ATTR_READONLY)
+ {
+ finfo.protection &= ~APR_UWRITE;
+ finfo.protection &= ~APR_GWRITE;
+ finfo.protection &= ~APR_WWRITE;
+ }
+ else
+ {
+ /* ### umask this! */
+ finfo.protection |= APR_UWRITE;
+ finfo.protection |= APR_GWRITE;
+ finfo.protection |= APR_WWRITE;
+ }
+ }
+
+ if (attr_mask & APR_FILE_ATTR_EXECUTABLE)
+ {
+ if (attributes & APR_FILE_ATTR_EXECUTABLE)
+ {
+ /* ### umask this! */
+ finfo.protection |= APR_UEXECUTE;
+ finfo.protection |= APR_GEXECUTE;
+ finfo.protection |= APR_WEXECUTE;
+ }
+ else
+ {
+ finfo.protection &= ~APR_UEXECUTE;
+ finfo.protection &= ~APR_GEXECUTE;
+ finfo.protection &= ~APR_WEXECUTE;
+ }
+ }
+
+ return apr_file_perms_set(fname, finfo.protection);
+}
+
+#ifndef APR_HAS_PSA
+static apr_status_t stat_cache_cleanup(void *data)
+{
+ apr_pool_t *p = (apr_pool_t *)getGlobalPool();
+ apr_hash_index_t *hi;
+ apr_hash_t *statCache = (apr_hash_t*)data;
+ char *key;
+ apr_ssize_t keylen;
+ NXPathCtx_t pathctx;
+
+ for (hi = apr_hash_first(p, statCache); hi; hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, (const void**)&key, &keylen, (void**)&pathctx);
+
+ if (pathctx) {
+ NXFreePathContext(pathctx);
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+int cstat (NXPathCtx_t ctx, char *path, struct stat *buf, unsigned long requestmap, apr_pool_t *p)
+{
+ apr_pool_t *gPool = (apr_pool_t *)getGlobalPool();
+ apr_hash_t *statCache = NULL;
+ apr_thread_rwlock_t *rwlock = NULL;
+
+ NXPathCtx_t pathctx = 0;
+ char *ptr = NULL, *tr;
+ int len = 0, x;
+ char *ppath;
+ char *pinfo;
+
+ if (ctx == 1) {
+
+ /* If there isn't a global pool then just stat the file
+ and return */
+ if (!gPool) {
+ char poolname[50];
+
+ if (apr_pool_create(&gPool, NULL) != APR_SUCCESS) {
+ return getstat(ctx, path, buf, requestmap);
+ }
+
+ setGlobalPool(gPool);
+ apr_pool_tag(gPool, apr_pstrdup(gPool, "cstat_mem_pool"));
+
+ statCache = apr_hash_make(gPool);
+ apr_pool_userdata_set ((void*)statCache, "STAT_CACHE", stat_cache_cleanup, gPool);
+
+ apr_thread_rwlock_create(&rwlock, gPool);
+ apr_pool_userdata_set ((void*)rwlock, "STAT_CACHE_LOCK", apr_pool_cleanup_null, gPool);
+ }
+ else {
+ apr_pool_userdata_get((void**)&statCache, "STAT_CACHE", gPool);
+ apr_pool_userdata_get((void**)&rwlock, "STAT_CACHE_LOCK", gPool);
+ }
+
+ if (!gPool || !statCache || !rwlock) {
+ return getstat(ctx, path, buf, requestmap);
+ }
+
+ for (x = 0,tr = path;*tr != '\0';tr++,x++) {
+ if (*tr == '\\' || *tr == '/') {
+ ptr = tr;
+ len = x;
+ }
+ if (*tr == ':') {
+ ptr = "\\";
+ len = x;
+ }
+ }
+
+ if (ptr) {
+ ppath = apr_pstrndup (p, path, len);
+ strlwr(ppath);
+ if (ptr[1] != '\0') {
+ ptr++;
+ }
+ /* If the path ended in a trailing slash then our result path
+ will be a single slash. To avoid stat'ing the root with a
+ slash, we need to make sure we stat the current directory
+ with a dot */
+ if (((*ptr == '/') || (*ptr == '\\')) && (*(ptr+1) == '\0')) {
+ pinfo = apr_pstrdup (p, ".");
+ }
+ else {
+ pinfo = apr_pstrdup (p, ptr);
+ }
+ }
+
+ /* If we have a statCache then try to pull the information
+ from the cache. Otherwise just stat the file and return.*/
+ if (statCache) {
+ apr_thread_rwlock_rdlock(rwlock);
+ pathctx = (NXPathCtx_t) apr_hash_get(statCache, ppath, APR_HASH_KEY_STRING);
+ apr_thread_rwlock_unlock(rwlock);
+ if (pathctx) {
+ return getstat(pathctx, pinfo, buf, requestmap);
+ }
+ else {
+ int err;
+
+ err = NXCreatePathContext(0, ppath, 0, NULL, &pathctx);
+ if (!err) {
+ apr_thread_rwlock_wrlock(rwlock);
+ apr_hash_set(statCache, apr_pstrdup(gPool,ppath) , APR_HASH_KEY_STRING, (void*)pathctx);
+ apr_thread_rwlock_unlock(rwlock);
+ return getstat(pathctx, pinfo, buf, requestmap);
+ }
+ }
+ }
+ }
+ return getstat(ctx, path, buf, requestmap);
+}
+#endif
+
+APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
+ const char *fname,
+ apr_int32_t wanted, apr_pool_t *pool)
+{
+ struct stat info;
+ int srv;
+ NXPathCtx_t pathCtx = 0;
+
+ getcwdpath(NULL, &pathCtx, CTX_ACTUAL_CWD);
+#ifdef APR_HAS_PSA
+ srv = getstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT);
+#else
+ srv = cstat(pathCtx, (char*)fname, &info, ST_STAT_BITS|ST_NAME_BIT, pool);
+#endif
+ errno = srv;
+
+ if (srv == 0) {
+ finfo->pool = pool;
+ finfo->fname = fname;
+ fill_out_finfo(finfo, &info, wanted);
+ if (wanted & APR_FINFO_LINK)
+ wanted &= ~APR_FINFO_LINK;
+ if (wanted & APR_FINFO_NAME) {
+ finfo->name = apr_pstrdup(pool, info.st_name);
+ finfo->valid |= APR_FINFO_NAME;
+ }
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+#if !defined(ENOENT) || !defined(ENOTDIR)
+#error ENOENT || ENOTDIR not defined; please see the
+#error comments at this line in the source for a workaround.
+ /*
+ * If ENOENT || ENOTDIR is not defined in one of the your OS's
+ * include files, APR cannot report a good reason why the stat()
+ * of the file failed; there are cases where it can fail even though
+ * the file exists. This opens holes in Apache, for example, because
+ * it becomes possible for someone to get a directory listing of a
+ * directory even though there is an index (eg. index.html) file in
+ * it. If you do not have a problem with this, delete the above
+ * #error lines and start the compile again. If you need to do this,
+ * please submit a bug report to http://www.apache.org/bug_report.html
+ * letting us know that you needed to do this. Please be sure to
+ * include the operating system you are using.
+ */
+ /* WARNING: All errors will be handled as not found
+ */
+#if !defined(ENOENT)
+ return APR_ENOENT;
+#else
+ /* WARNING: All errors but not found will be handled as not directory
+ */
+ if (errno != ENOENT)
+ return APR_ENOENT;
+ else
+ return errno;
+#endif
+#else /* All was defined well, report the usual: */
+ return errno;
+#endif
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
+ apr_time_t mtime,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
+ apr_finfo_t finfo;
+
+ status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool);
+ if (status) {
+ return status;
+ }
+
+#ifdef HAVE_UTIMES
+ {
+ struct timeval tvp[2];
+
+ tvp[0].tv_sec = apr_time_sec(finfo.atime);
+ tvp[0].tv_usec = apr_time_usec(finfo.atime);
+ tvp[1].tv_sec = apr_time_sec(mtime);
+ tvp[1].tv_usec = apr_time_usec(mtime);
+
+ if (utimes(fname, tvp) == -1) {
+ return errno;
+ }
+ }
+#elif defined(HAVE_UTIME)
+ {
+ struct utimbuf buf;
+
+ buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC);
+ buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC);
+
+ if (utime(fname, &buf) == -1) {
+ return errno;
+ }
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+
+ return APR_SUCCESS;
+}
diff --git a/file_io/netware/filesys.c b/file_io/netware/filesys.c
new file mode 100644
index 0000000..05c44ce
--- /dev/null
+++ b/file_io/netware/filesys.c
@@ -0,0 +1,106 @@
+/* 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"
+
+apr_status_t filepath_root_case(char **rootpath, char *root, apr_pool_t *p)
+{
+/* See the Windows code to figure out what to do here.
+ It probably checks to make sure that the root exists
+ and case it correctly according to the file system.
+*/
+ *rootpath = apr_pstrdup(p, root);
+ return APR_SUCCESS;
+}
+
+apr_status_t filepath_has_drive(const char *rootpath, int only, apr_pool_t *p)
+{
+ char *s;
+
+ if (rootpath) {
+ s = strchr (rootpath, ':');
+ if (only)
+ /* Test if the path only has a drive/volume and nothing else
+ */
+ return (s && (s != rootpath) && !s[1]);
+ else
+ /* Test if the path includes a drive/volume
+ */
+ return (s && (s != rootpath));
+ }
+ return 0;
+}
+
+apr_status_t filepath_compare_drive(const char *path1, const char *path2, apr_pool_t *p)
+{
+ char *s1, *s2;
+
+ if (path1 && path2) {
+ s1 = strchr (path1, ':');
+ s2 = strchr (path2, ':');
+
+ /* Make sure that they both have a drive/volume delimiter
+ and are the same size. Then see if they match.
+ */
+ if (s1 && s2 && ((s1-path1) == (s2-path2))) {
+ return strnicmp (s1, s2, s1-path1);
+ }
+ }
+ return -1;
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_get(char **rootpath, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char path[APR_PATH_MAX];
+ char *ptr;
+
+ /* use getcwdpath to make sure that we get the volume name*/
+ if (!getcwdpath(path, NULL, 0)) {
+ if (errno == ERANGE)
+ return APR_ENAMETOOLONG;
+ else
+ return errno;
+ }
+ /* Strip off the server name if there is one*/
+ ptr = strpbrk(path, "\\/:");
+ if (!ptr) {
+ return APR_ENOENT;
+ }
+ if (*ptr == ':') {
+ ptr = path;
+ }
+ *rootpath = apr_pstrdup(p, ptr);
+ if (!(flags & APR_FILEPATH_NATIVE)) {
+ for (ptr = *rootpath; *ptr; ++ptr) {
+ if (*ptr == '\\')
+ *ptr = '/';
+ }
+ }
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_set(const char *rootpath,
+ apr_pool_t *p)
+{
+ if (chdir2(rootpath) != 0)
+ return errno;
+ return APR_SUCCESS;
+}
+
+
diff --git a/file_io/netware/flock.c b/file_io/netware/flock.c
new file mode 100644
index 0000000..c083a0e
--- /dev/null
+++ b/file_io/netware/flock.c
@@ -0,0 +1,39 @@
+/* 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 <nks/fsio.h>
+#include "apr_arch_file_io.h"
+
+
+apr_status_t apr_file_lock(apr_file_t *thefile, int type)
+{
+ int fc;
+
+ fc = (type & APR_FLOCK_NONBLOCK) ? NX_RANGE_LOCK_TRYLOCK : NX_RANGE_LOCK_CHECK;
+
+ if(NXFileRangeLock(thefile->filedes,fc, 0, 0) == -1)
+ return errno;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_file_unlock(apr_file_t *thefile)
+{
+ if(NXFileRangeUnlock(thefile->filedes,NX_RANGE_LOCK_CANCEL,0 , 0) == -1)
+ return errno;
+
+ return APR_SUCCESS;
+}
diff --git a/file_io/netware/mktemp.c b/file_io/netware/mktemp.c
new file mode 100644
index 0000000..4f78226
--- /dev/null
+++ b/file_io/netware/mktemp.c
@@ -0,0 +1,64 @@
+/* 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_file_io.h" /* prototype of apr_mkstemp() */
+#include "apr_strings.h" /* prototype of apr_mkstemp() */
+#include "apr_arch_file_io.h" /* prototype of apr_mkstemp() */
+#include "apr_portable.h" /* for apr_os_file_put() */
+#include "apr_arch_inherit.h"
+
+#include <stdlib.h> /* for mkstemp() - Single Unix */
+
+APR_DECLARE(apr_status_t) apr_file_mktemp(apr_file_t **fp, char *template, apr_int32_t flags, apr_pool_t *p)
+{
+ int fd;
+ apr_status_t rv;
+
+ flags = (!flags) ? APR_FOPEN_CREATE | APR_FOPEN_READ | APR_FOPEN_WRITE |
+ APR_FOPEN_DELONCLOSE : flags & ~APR_FOPEN_EXCL;
+
+ fd = mkstemp(template);
+ if (fd == -1) {
+ return errno;
+ }
+ /* We need to reopen the file to get rid of the o_excl flag.
+ * Otherwise file locking will not allow the file to be shared.
+ */
+ close(fd);
+ if ((rv = apr_file_open(fp, template, flags|APR_FOPEN_NOCLEANUP,
+ APR_UREAD | APR_UWRITE, p)) == APR_SUCCESS) {
+
+
+ if (!(flags & APR_FOPEN_NOCLEANUP)) {
+ int flags;
+
+ if ((flags = fcntl((*fp)->filedes, F_GETFD)) == -1)
+ return errno;
+
+ flags |= FD_CLOEXEC;
+ if (fcntl((*fp)->filedes, F_SETFD, flags) == -1)
+ return errno;
+
+ apr_pool_cleanup_register((*fp)->pool, (void *)(*fp),
+ apr_unix_file_cleanup,
+ apr_unix_child_file_cleanup);
+ }
+ }
+
+ return rv;
+}
+
diff --git a/file_io/netware/pipe.c b/file_io/netware/pipe.c
new file mode 100644
index 0000000..26e09d7
--- /dev/null
+++ b/file_io/netware/pipe.c
@@ -0,0 +1,239 @@
+/* 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 <stdio.h>
+#include <nks/fsio.h>
+#include <nks/errno.h>
+
+#include "apr_arch_file_io.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_arch_inherit.h"
+
+static apr_status_t pipeblock(apr_file_t *thepipe)
+{
+#ifdef USE_FLAGS
+ unsigned long flags;
+
+ if (fcntl(thepipe->filedes, F_GETFL, &flags) != -1)
+ {
+ flags &= ~FNDELAY;
+ fcntl(thepipe->filedes, F_SETFL, flags);
+ }
+#else
+ errno = 0;
+ fcntl(thepipe->filedes, F_SETFL, 0);
+#endif
+
+ if (errno)
+ return errno;
+
+ thepipe->blocking = BLK_ON;
+ return APR_SUCCESS;
+}
+
+static apr_status_t pipenonblock(apr_file_t *thepipe)
+{
+#ifdef USE_FLAGS
+ unsigned long flags;
+
+ errno = 0;
+ if (fcntl(thepipe->filedes, F_GETFL, &flags) != -1)
+ {
+ flags |= FNDELAY;
+ fcntl(thepipe->filedes, F_SETFL, flags);
+ }
+#else
+ errno = 0;
+ fcntl(thepipe->filedes, F_SETFL, FNDELAY);
+#endif
+
+ if (errno)
+ return errno;
+
+ thepipe->blocking = BLK_OFF;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_set(apr_file_t *thepipe, apr_interval_time_t timeout)
+{
+ if (thepipe->is_pipe == 1) {
+ thepipe->timeout = timeout;
+ if (timeout >= 0) {
+ if (thepipe->blocking != BLK_OFF) { /* blocking or unknown state */
+ return pipenonblock(thepipe);
+ }
+ }
+ else {
+ if (thepipe->blocking != BLK_ON) { /* non-blocking or unknown state */
+ return pipeblock(thepipe);
+ }
+ }
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_get(apr_file_t *thepipe, apr_interval_time_t *timeout)
+{
+ if (thepipe->is_pipe == 1) {
+ *timeout = thepipe->timeout;
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+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)
+{
+ int *dafile = thefile;
+
+ (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
+ (*file)->pool = pool;
+ (*file)->eof_hit = 0;
+ (*file)->is_pipe = 1;
+ (*file)->blocking = BLK_UNKNOWN; /* app needs to make a timeout call */
+ (*file)->timeout = -1;
+ (*file)->ungetchar = -1; /* no char avail */
+ (*file)->filedes = *dafile;
+ if (!register_cleanup) {
+ (*file)->flags = APR_FOPEN_NOCLEANUP;
+ }
+ (*file)->buffered = 0;
+#if APR_HAS_THREADS
+ (*file)->thlock = NULL;
+#endif
+ if (register_cleanup) {
+ apr_pool_cleanup_register((*file)->pool, (void *)(*file),
+ apr_unix_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 file_pipe_create(apr_file_t **in, apr_file_t **out,
+ apr_pool_t *pool_in, apr_pool_t *pool_out)
+{
+ int filedes[2];
+
+ if (pipe(filedes) == -1) {
+ return errno;
+ }
+
+ (*in) = (apr_file_t *)apr_pcalloc(pool_in, sizeof(apr_file_t));
+ (*out) = (apr_file_t *)apr_pcalloc(pool_out, sizeof(apr_file_t));
+
+ (*in)->pool = pool_in;
+ (*out)->pool = pool_out;
+ (*in)->filedes = filedes[0];
+ (*out)->filedes = filedes[1];
+ (*in)->flags = APR_INHERIT;
+ (*out)->flags = APR_INHERIT;
+ (*in)->is_pipe =
+ (*out)->is_pipe = 1;
+ (*out)->fname =
+ (*in)->fname = NULL;
+ (*in)->buffered =
+ (*out)->buffered = 0;
+ (*in)->blocking =
+ (*out)->blocking = BLK_ON;
+ (*in)->timeout =
+ (*out)->timeout = -1;
+ (*in)->ungetchar = -1;
+ (*in)->thlock =
+ (*out)->thlock = NULL;
+ (void) apr_pollset_create(&(*in)->pollset, 1, pool_in, 0);
+ (void) apr_pollset_create(&(*out)->pollset, 1, pool_out, 0);
+
+ apr_pool_cleanup_register((*in)->pool, (void *)(*in), apr_unix_file_cleanup,
+ apr_pool_cleanup_null);
+ apr_pool_cleanup_register((*out)->pool, (void *)(*out), apr_unix_file_cleanup,
+ apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+static void file_pipe_block(apr_file_t **in, apr_file_t **out,
+ apr_int32_t blocking)
+{
+ switch (blocking) {
+ case APR_FULL_BLOCK:
+ break;
+ case APR_READ_BLOCK:
+ apr_file_pipe_timeout_set(*out, 0);
+ break;
+ case APR_WRITE_BLOCK:
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ default:
+ apr_file_pipe_timeout_set(*out, 0);
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_create(apr_file_t **in, apr_file_t **out, apr_pool_t *pool)
+{
+ return file_pipe_create(in, out, pool, pool);
+}
+
+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 *pool)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool, pool)) != APR_SUCCESS) {
+ return status;
+ }
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+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)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool_in, pool_out)) != APR_SUCCESS) {
+ return status;
+ }
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_namedpipe_create(const char *filename,
+ apr_fileperms_t perm, apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+
+
diff --git a/file_io/os2/buffer.c b/file_io/os2/buffer.c
new file mode 100644
index 0000000..34e4e63
--- /dev/null
+++ b/file_io/os2/buffer.c
@@ -0,0 +1,59 @@
+/* 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;
+
+ apr_thread_mutex_lock(file->mutex);
+
+ if(file->buffered) {
+ /* Flush the existing buffer */
+ rv = apr_file_flush(file);
+ if (rv != APR_SUCCESS) {
+ 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;
+ }
+
+ 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;
+}
diff --git a/file_io/os2/copy.c b/file_io/os2/copy.c
new file mode 100644
index 0000000..f4ce010
--- /dev/null
+++ b/file_io/os2/copy.c
@@ -0,0 +1 @@
+#include "../unix/copy.c"
diff --git a/file_io/os2/dir.c b/file_io/os2/dir.c
new file mode 100644
index 0000000..3b08355
--- /dev/null
+++ b/file_io/os2/dir.c
@@ -0,0 +1,167 @@
+/* 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_lib.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include <string.h>
+
+static apr_status_t dir_cleanup(void *thedir)
+{
+ apr_dir_t *dir = thedir;
+ return apr_dir_close(dir);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_open(apr_dir_t **new, const char *dirname, apr_pool_t *pool)
+{
+ apr_dir_t *thedir = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
+
+ if (thedir == NULL)
+ return APR_ENOMEM;
+
+ thedir->pool = pool;
+ thedir->dirname = apr_pstrdup(pool, dirname);
+
+ if (thedir->dirname == NULL)
+ return APR_ENOMEM;
+
+ thedir->handle = 0;
+ thedir->validentry = FALSE;
+ *new = thedir;
+ apr_pool_cleanup_register(pool, thedir, dir_cleanup, apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_close(apr_dir_t *thedir)
+{
+ int rv = 0;
+
+ if (thedir->handle) {
+ rv = DosFindClose(thedir->handle);
+
+ if (rv == 0) {
+ thedir->handle = 0;
+ }
+ }
+
+ return APR_FROM_OS_ERROR(rv);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
+ apr_dir_t *thedir)
+{
+ int rv;
+ ULONG entries = 1;
+
+ if (thedir->handle == 0) {
+ thedir->handle = HDIR_CREATE;
+ rv = DosFindFirst(apr_pstrcat(thedir->pool, thedir->dirname, "/*", NULL), &thedir->handle,
+ FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY,
+ &thedir->entry, sizeof(thedir->entry), &entries, FIL_STANDARD);
+ } else {
+ rv = DosFindNext(thedir->handle, &thedir->entry, sizeof(thedir->entry), &entries);
+ }
+
+ finfo->pool = thedir->pool;
+ finfo->fname = NULL;
+ finfo->valid = 0;
+
+ if (rv == 0 && entries == 1) {
+ thedir->validentry = TRUE;
+
+ /* We passed a name off the stack that has popped */
+ finfo->fname = NULL;
+ finfo->size = thedir->entry.cbFile;
+ finfo->csize = thedir->entry.cbFileAlloc;
+
+ /* Only directories & regular files show up in directory listings */
+ finfo->filetype = (thedir->entry.attrFile & FILE_DIRECTORY) ? APR_DIR : APR_REG;
+
+ apr_os2_time_to_apr_time(&finfo->mtime, thedir->entry.fdateLastWrite,
+ thedir->entry.ftimeLastWrite);
+ apr_os2_time_to_apr_time(&finfo->atime, thedir->entry.fdateLastAccess,
+ thedir->entry.ftimeLastAccess);
+ apr_os2_time_to_apr_time(&finfo->ctime, thedir->entry.fdateCreation,
+ thedir->entry.ftimeCreation);
+
+ finfo->name = thedir->entry.achName;
+ finfo->valid = APR_FINFO_NAME | APR_FINFO_MTIME | APR_FINFO_ATIME |
+ APR_FINFO_CTIME | APR_FINFO_TYPE | APR_FINFO_SIZE |
+ APR_FINFO_CSIZE;
+
+ return APR_SUCCESS;
+ }
+
+ thedir->validentry = FALSE;
+
+ if (rv)
+ return APR_FROM_OS_ERROR(rv);
+
+ return APR_ENOENT;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_rewind(apr_dir_t *thedir)
+{
+ return apr_dir_close(thedir);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_make(const char *path, apr_fileperms_t perm, apr_pool_t *pool)
+{
+ return APR_FROM_OS_ERROR(DosCreateDir(path, NULL));
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_dir_remove(const char *path, apr_pool_t *pool)
+{
+ return APR_FROM_OS_ERROR(DosDeleteDir(path));
+}
+
+
+
+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->handle;
+ 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)
+{
+ if ((*dir) == NULL) {
+ (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
+ (*dir)->pool = pool;
+ }
+ (*dir)->handle = *thedir;
+ return APR_SUCCESS;
+}
diff --git a/file_io/os2/dir_make_recurse.c b/file_io/os2/dir_make_recurse.c
new file mode 100644
index 0000000..602a621
--- /dev/null
+++ b/file_io/os2/dir_make_recurse.c
@@ -0,0 +1,90 @@
+/* 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_file_io.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include <string.h>
+
+#define IS_SEP(c) (c == '/' || c == '\\')
+
+/* Remove trailing separators that don't affect the meaning of PATH. */
+static const char *path_canonicalize(const char *path, apr_pool_t *pool)
+{
+ /* At some point this could eliminate redundant components. For
+ * now, it just makes sure there is no trailing slash. */
+ apr_size_t len = strlen(path);
+ apr_size_t orig_len = len;
+
+ while ((len > 0) && IS_SEP(path[len - 1])) {
+ len--;
+ }
+
+ if (len != orig_len) {
+ return apr_pstrndup(pool, path, len);
+ }
+ else {
+ return path;
+ }
+}
+
+
+
+/* Remove one component off the end of PATH. */
+static char *path_remove_last_component(const char *path, apr_pool_t *pool)
+{
+ const char *newpath = path_canonicalize(path, pool);
+ int i;
+
+ for (i = strlen(newpath) - 1; i >= 0; i--) {
+ if (IS_SEP(path[i])) {
+ break;
+ }
+ }
+
+ return apr_pstrndup(pool, path, (i < 0) ? 0 : i);
+}
+
+
+
+apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
+ apr_pool_t *pool)
+{
+ apr_status_t apr_err = APR_SUCCESS;
+
+ apr_err = apr_dir_make(path, perm, pool); /* Try to make PATH right out */
+
+ if (APR_STATUS_IS_ENOENT(apr_err)) { /* Missing an intermediate dir */
+ char *dir;
+
+ dir = path_remove_last_component(path, pool);
+ apr_err = apr_dir_make_recursive(dir, perm, pool);
+
+ if (!apr_err) {
+ apr_err = apr_dir_make(path, perm, pool);
+ }
+ }
+
+ /*
+ * 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.
+ */
+ if (APR_STATUS_IS_EEXIST(apr_err))
+ return APR_SUCCESS;
+
+ return apr_err;
+}
diff --git a/file_io/os2/fileacc.c b/file_io/os2/fileacc.c
new file mode 100644
index 0000000..b5c1afd
--- /dev/null
+++ b/file_io/os2/fileacc.c
@@ -0,0 +1,18 @@
+/* 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 "../unix/fileacc.c"
+
diff --git a/file_io/os2/filedup.c b/file_io/os2/filedup.c
new file mode 100644
index 0000000..86f06f3
--- /dev/null
+++ b/file_io/os2/filedup.c
@@ -0,0 +1,124 @@
+/* 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_lib.h"
+#include "apr_strings.h"
+#include <string.h>
+#include "apr_arch_inherit.h"
+
+static apr_status_t file_dup(apr_file_t **new_file, apr_file_t *old_file, apr_pool_t *p)
+{
+ int rv;
+ apr_file_t *dup_file;
+
+ if (*new_file == NULL) {
+ dup_file = (apr_file_t *)apr_palloc(p, sizeof(apr_file_t));
+
+ if (dup_file == NULL) {
+ return APR_ENOMEM;
+ }
+
+ dup_file->filedes = -1;
+ } else {
+ dup_file = *new_file;
+ }
+
+ dup_file->pool = p;
+ rv = DosDupHandle(old_file->filedes, &dup_file->filedes);
+
+ if (rv) {
+ return APR_FROM_OS_ERROR(rv);
+ }
+
+ dup_file->fname = apr_pstrdup(dup_file->pool, old_file->fname);
+ dup_file->buffered = old_file->buffered;
+ dup_file->isopen = old_file->isopen;
+ dup_file->flags = old_file->flags & ~APR_INHERIT;
+ /* TODO - dup pipes correctly */
+ dup_file->pipe = old_file->pipe;
+
+ if (*new_file == NULL) {
+ apr_pool_cleanup_register(dup_file->pool, dup_file, apr_file_cleanup,
+ apr_pool_cleanup_null);
+ *new_file = dup_file;
+ }
+
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_dup(apr_file_t **new_file, apr_file_t *old_file, apr_pool_t *p)
+{
+ if (*new_file) {
+ apr_file_close(*new_file);
+ (*new_file)->filedes = -1;
+ }
+
+ return file_dup(new_file, old_file, p);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_dup2(apr_file_t *new_file, apr_file_t *old_file, apr_pool_t *p)
+{
+ return file_dup(&new_file, old_file, p);
+}
+
+
+
+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_kill(old_file->pool, (void *)old_file,
+ apr_file_cleanup);
+ apr_pool_cleanup_register(p, (void *)(*new_file),
+ apr_file_cleanup,
+ apr_file_cleanup);
+ }
+
+ old_file->filedes = -1;
+ return APR_SUCCESS;
+}
diff --git a/file_io/os2/filepath.c b/file_io/os2/filepath.c
new file mode 100644
index 0000000..9422faa
--- /dev/null
+++ b/file_io/os2/filepath.c
@@ -0,0 +1,16 @@
+/* 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 "../win32/filepath.c"
diff --git a/file_io/os2/filepath_util.c b/file_io/os2/filepath_util.c
new file mode 100644
index 0000000..a89c173
--- /dev/null
+++ b/file_io/os2/filepath_util.c
@@ -0,0 +1 @@
+#include "../unix/filepath_util.c"
diff --git a/file_io/os2/filestat.c b/file_io/os2/filestat.c
new file mode 100644
index 0000000..cd163e4
--- /dev/null
+++ b/file_io/os2/filestat.c
@@ -0,0 +1,241 @@
+/* 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.
+ */
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+#include "apr_arch_file_io.h"
+#include "apr_file_io.h"
+#include "apr_lib.h"
+#include <sys/time.h>
+#include "apr_strings.h"
+
+
+static void FS3_to_finfo(apr_finfo_t *finfo, FILESTATUS3 *fstatus)
+{
+ finfo->protection = (fstatus->attrFile & FILE_READONLY) ? 0x555 : 0x777;
+
+ if (fstatus->attrFile & FILE_DIRECTORY)
+ finfo->filetype = APR_DIR;
+ else
+ finfo->filetype = APR_REG;
+ /* XXX: No other possible types from FS3? */
+
+ finfo->user = 0;
+ finfo->group = 0;
+ finfo->inode = 0;
+ finfo->device = 0;
+ finfo->size = fstatus->cbFile;
+ finfo->csize = fstatus->cbFileAlloc;
+ apr_os2_time_to_apr_time(&finfo->atime, fstatus->fdateLastAccess,
+ fstatus->ftimeLastAccess );
+ apr_os2_time_to_apr_time(&finfo->mtime, fstatus->fdateLastWrite,
+ fstatus->ftimeLastWrite );
+ apr_os2_time_to_apr_time(&finfo->ctime, fstatus->fdateCreation,
+ fstatus->ftimeCreation );
+ finfo->valid = APR_FINFO_TYPE | APR_FINFO_PROT | APR_FINFO_SIZE
+ | APR_FINFO_CSIZE | APR_FINFO_MTIME
+ | APR_FINFO_CTIME | APR_FINFO_ATIME | APR_FINFO_LINK;
+}
+
+
+
+static apr_status_t handle_type(apr_filetype_e *ftype, HFILE file)
+{
+ ULONG filetype, fileattr, rc;
+
+ rc = DosQueryHType(file, &filetype, &fileattr);
+
+ if (rc == 0) {
+ switch (filetype & 0xff) {
+ case 0:
+ *ftype = APR_REG;
+ break;
+
+ case 1:
+ *ftype = APR_CHR;
+ break;
+
+ case 2:
+ *ftype = APR_PIPE;
+ break;
+
+ default:
+ /* Brian, is this correct???
+ */
+ *ftype = APR_UNKFILE;
+ break;
+ }
+
+ return APR_SUCCESS;
+ }
+ return APR_FROM_OS_ERROR(rc);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo, apr_int32_t wanted,
+ apr_file_t *thefile)
+{
+ ULONG rc;
+ FILESTATUS3 fstatus;
+
+ if (thefile->isopen) {
+ if (thefile->buffered) {
+ /* XXX: flush here is not mutex protected */
+ apr_status_t rv = apr_file_flush(thefile);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ rc = DosQueryFileInfo(thefile->filedes, FIL_STANDARD, &fstatus, sizeof(fstatus));
+ }
+ else
+ rc = DosQueryPathInfo(thefile->fname, FIL_STANDARD, &fstatus, sizeof(fstatus));
+
+ if (rc == 0) {
+ FS3_to_finfo(finfo, &fstatus);
+ finfo->fname = thefile->fname;
+
+ if (finfo->filetype == APR_REG) {
+ if (thefile->isopen) {
+ return handle_type(&finfo->filetype, thefile->filedes);
+ }
+ } else {
+ return APR_SUCCESS;
+ }
+ }
+
+ finfo->protection = 0;
+ finfo->filetype = APR_NOFILE;
+ return APR_FROM_OS_ERROR(rc);
+}
+
+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 *cont)
+{
+ ULONG rc;
+ FILESTATUS3 fstatus;
+
+ finfo->protection = 0;
+ finfo->filetype = APR_NOFILE;
+ finfo->name = NULL;
+ rc = DosQueryPathInfo(fname, FIL_STANDARD, &fstatus, sizeof(fstatus));
+
+ if (rc == 0) {
+ FS3_to_finfo(finfo, &fstatus);
+ finfo->fname = fname;
+
+ if (wanted & APR_FINFO_NAME) {
+ ULONG count = 1;
+ HDIR hDir = HDIR_SYSTEM;
+ FILEFINDBUF3 ffb;
+ rc = DosFindFirst(fname, &hDir,
+ FILE_DIRECTORY|FILE_HIDDEN|FILE_SYSTEM|FILE_ARCHIVED,
+ &ffb, sizeof(ffb), &count, FIL_STANDARD);
+ if (rc == 0 && count == 1) {
+ finfo->name = apr_pstrdup(cont, ffb.achName);
+ finfo->valid |= APR_FINFO_NAME;
+ }
+ }
+ } else if (rc == ERROR_INVALID_ACCESS) {
+ memset(finfo, 0, sizeof(apr_finfo_t));
+ finfo->valid = APR_FINFO_TYPE | APR_FINFO_PROT;
+ finfo->protection = 0666;
+ finfo->filetype = APR_CHR;
+
+ if (wanted & APR_FINFO_NAME) {
+ finfo->name = apr_pstrdup(cont, fname);
+ finfo->valid |= APR_FINFO_NAME;
+ }
+ } else {
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : 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 *cont)
+{
+ FILESTATUS3 fs3;
+ ULONG rc;
+
+ /* 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;
+
+ rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs3, sizeof(fs3));
+ if (rc == 0) {
+ ULONG old_attr = fs3.attrFile;
+
+ if (attr_mask & APR_FILE_ATTR_READONLY)
+ {
+ if (attributes & APR_FILE_ATTR_READONLY) {
+ fs3.attrFile |= FILE_READONLY;
+ } else {
+ fs3.attrFile &= ~FILE_READONLY;
+ }
+ }
+
+ if (attr_mask & APR_FILE_ATTR_HIDDEN)
+ {
+ if (attributes & APR_FILE_ATTR_HIDDEN) {
+ fs3.attrFile |= FILE_HIDDEN;
+ } else {
+ fs3.attrFile &= ~FILE_HIDDEN;
+ }
+ }
+
+ if (fs3.attrFile != old_attr) {
+ rc = DosSetPathInfo(fname, FIL_STANDARD, &fs3, sizeof(fs3), 0);
+ }
+ }
+
+ return APR_FROM_OS_ERROR(rc);
+}
+
+
+/* ### Somebody please write this! */
+APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
+ apr_time_t mtime,
+ apr_pool_t *pool)
+{
+ FILESTATUS3 fs3;
+ ULONG rc;
+ rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs3, sizeof(fs3));
+
+ if (rc) {
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ apr_apr_time_to_os2_time(&fs3.fdateLastWrite, &fs3.ftimeLastWrite, mtime);
+
+ rc = DosSetPathInfo(fname, FIL_STANDARD, &fs3, sizeof(fs3), 0);
+ return APR_FROM_OS_ERROR(rc);
+}
diff --git a/file_io/os2/filesys.c b/file_io/os2/filesys.c
new file mode 100644
index 0000000..ae43bc0
--- /dev/null
+++ b/file_io/os2/filesys.c
@@ -0,0 +1,148 @@
+/* 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"
+#include "apr_lib.h"
+#include <ctype.h>
+
+/* OS/2 Exceptions:
+ *
+ * Note that trailing spaces and trailing periods are never recorded
+ * in the file system.
+ *
+ * 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 ;)
+ */
+
+const char c_is_fnchar[256] =
+{/* Reject all ctrl codes... */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ /* " * / : < > ? */
+ 1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0, 1,1,1,1,1,1,1,1,1,1,0,1,0,1,0,0,
+ /* \ */
+ 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,0,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,0,1,1,1,
+ /* High bit codes are accepted */
+ 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
+};
+
+
+#define IS_SLASH(c) (c == '/' || c == '\\')
+
+
+apr_status_t filepath_root_test(char *path, apr_pool_t *p)
+{
+ char drive = apr_toupper(path[0]);
+
+ if (drive >= 'A' && drive <= 'Z' && path[1] == ':' && IS_SLASH(path[2]))
+ return APR_SUCCESS;
+
+ return APR_EBADPATH;
+}
+
+
+apr_status_t filepath_drive_get(char **rootpath, char drive,
+ apr_int32_t flags, apr_pool_t *p)
+{
+ char path[APR_PATH_MAX];
+ char *pos;
+ ULONG rc;
+ ULONG bufsize = sizeof(path) - 3;
+
+ path[0] = drive;
+ path[1] = ':';
+ path[2] = '/';
+
+ rc = DosQueryCurrentDir(apr_toupper(drive) - 'A', path+3, &bufsize);
+
+ if (rc) {
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ if (!(flags & APR_FILEPATH_NATIVE)) {
+ for (pos=path; *pos; pos++) {
+ if (*pos == '\\')
+ *pos = '/';
+ }
+ }
+
+ *rootpath = apr_pstrdup(p, path);
+ return APR_SUCCESS;
+}
+
+
+apr_status_t filepath_root_case(char **rootpath, char *root, apr_pool_t *p)
+{
+ if (root[0] && apr_islower(root[0]) && root[1] == ':') {
+ *rootpath = apr_pstrdup(p, root);
+ (*rootpath)[0] = apr_toupper((*rootpath)[0]);
+ }
+ else {
+ *rootpath = root;
+ }
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char path[APR_PATH_MAX];
+ ULONG drive;
+ ULONG drivemap;
+ ULONG rv, pathlen = sizeof(path) - 3;
+ char *pos;
+
+ DosQueryCurrentDisk(&drive, &drivemap);
+ path[0] = '@' + drive;
+ strcpy(path+1, ":\\");
+ rv = DosQueryCurrentDir(drive, path+3, &pathlen);
+
+ *defpath = apr_pstrdup(p, path);
+
+ if (!(flags & APR_FILEPATH_NATIVE)) {
+ for (pos=*defpath; *pos; pos++) {
+ if (*pos == '\\')
+ *pos = '/';
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
+{
+ ULONG rv = 0;
+
+ if (path[1] == ':')
+ rv = DosSetDefaultDisk(apr_toupper(path[0]) - '@');
+
+ if (rv == 0)
+ rv = DosSetCurrentDir(path);
+
+ return APR_FROM_OS_ERROR(rv);
+}
diff --git a/file_io/os2/flock.c b/file_io/os2/flock.c
new file mode 100644
index 0000000..ec94022
--- /dev/null
+++ b/file_io/os2/flock.c
@@ -0,0 +1,37 @@
+/* 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)
+{
+ FILELOCK lockrange = { 0, 0x7fffffff };
+ ULONG rc;
+
+ rc = DosSetFileLocks(thefile->filedes, NULL, &lockrange,
+ (type & APR_FLOCK_NONBLOCK) ? 0 : (ULONG)-1,
+ (type & APR_FLOCK_TYPEMASK) == APR_FLOCK_SHARED);
+ return APR_FROM_OS_ERROR(rc);
+}
+
+APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile)
+{
+ FILELOCK unlockrange = { 0, 0x7fffffff };
+ ULONG rc;
+
+ rc = DosSetFileLocks(thefile->filedes, &unlockrange, NULL, 0, 0);
+ return APR_FROM_OS_ERROR(rc);
+}
diff --git a/file_io/os2/fullrw.c b/file_io/os2/fullrw.c
new file mode 100644
index 0000000..cf62948
--- /dev/null
+++ b/file_io/os2/fullrw.c
@@ -0,0 +1 @@
+#include "../unix/fullrw.c"
diff --git a/file_io/os2/maperrorcode.c b/file_io/os2/maperrorcode.c
new file mode 100644
index 0000000..282338b
--- /dev/null
+++ b/file_io/os2/maperrorcode.c
@@ -0,0 +1,95 @@
+/* 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.
+ */
+
+#define INCL_DOSERRORS
+#include "apr_arch_file_io.h"
+#include "apr_file_io.h"
+#include <errno.h>
+#include <string.h>
+#include "apr_errno.h"
+
+static int errormap[][2] = {
+ { NO_ERROR, APR_SUCCESS },
+ { ERROR_FILE_NOT_FOUND, APR_ENOENT },
+ { ERROR_PATH_NOT_FOUND, APR_ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, APR_EMFILE },
+ { ERROR_ACCESS_DENIED, APR_EACCES },
+ { ERROR_SHARING_VIOLATION, APR_EACCES },
+ { ERROR_INVALID_PARAMETER, APR_EINVAL },
+ { ERROR_OPEN_FAILED, APR_ENOENT },
+ { ERROR_DISK_FULL, APR_ENOSPC },
+ { ERROR_FILENAME_EXCED_RANGE, APR_ENAMETOOLONG },
+ { ERROR_INVALID_FUNCTION, APR_EINVAL },
+ { ERROR_INVALID_HANDLE, APR_EBADF },
+ { ERROR_NEGATIVE_SEEK, APR_ESPIPE },
+ { ERROR_NO_SIGNAL_SENT, ESRCH },
+ { ERROR_NO_DATA, APR_EAGAIN },
+ { SOCEINTR, EINTR },
+ { SOCEWOULDBLOCK, EWOULDBLOCK },
+ { SOCEINPROGRESS, EINPROGRESS },
+ { SOCEALREADY, EALREADY },
+ { SOCENOTSOCK, ENOTSOCK },
+ { SOCEDESTADDRREQ, EDESTADDRREQ },
+ { SOCEMSGSIZE, EMSGSIZE },
+ { SOCEPROTOTYPE, EPROTOTYPE },
+ { SOCENOPROTOOPT, ENOPROTOOPT },
+ { SOCEPROTONOSUPPORT, EPROTONOSUPPORT },
+ { SOCESOCKTNOSUPPORT, ESOCKTNOSUPPORT },
+ { SOCEOPNOTSUPP, EOPNOTSUPP },
+ { SOCEPFNOSUPPORT, EPFNOSUPPORT },
+ { SOCEAFNOSUPPORT, EAFNOSUPPORT },
+ { SOCEADDRINUSE, EADDRINUSE },
+ { SOCEADDRNOTAVAIL, EADDRNOTAVAIL },
+ { SOCENETDOWN, ENETDOWN },
+ { SOCENETUNREACH, ENETUNREACH },
+ { SOCENETRESET, ENETRESET },
+ { SOCECONNABORTED, ECONNABORTED },
+ { SOCECONNRESET, ECONNRESET },
+ { SOCENOBUFS, ENOBUFS },
+ { SOCEISCONN, EISCONN },
+ { SOCENOTCONN, ENOTCONN },
+ { SOCESHUTDOWN, ESHUTDOWN },
+ { SOCETOOMANYREFS, ETOOMANYREFS },
+ { SOCETIMEDOUT, ETIMEDOUT },
+ { SOCECONNREFUSED, ECONNREFUSED },
+ { SOCELOOP, ELOOP },
+ { SOCENAMETOOLONG, ENAMETOOLONG },
+ { SOCEHOSTDOWN, EHOSTDOWN },
+ { SOCEHOSTUNREACH, EHOSTUNREACH },
+ { SOCENOTEMPTY, ENOTEMPTY },
+ { SOCEPIPE, EPIPE }
+};
+
+#define MAPSIZE (sizeof(errormap)/sizeof(errormap[0]))
+
+int apr_canonical_error(apr_status_t err)
+{
+ int rv = -1, index;
+
+ if (err < APR_OS_START_SYSERR)
+ return err;
+
+ err -= APR_OS_START_SYSERR;
+
+ for (index=0; index<MAPSIZE && errormap[index][0] != err; index++);
+
+ if (index<MAPSIZE)
+ rv = errormap[index][1];
+ else
+ fprintf(stderr, "apr_canonical_error: Unknown OS/2 error code %d\n", err );
+
+ return rv;
+}
diff --git a/file_io/os2/mktemp.c b/file_io/os2/mktemp.c
new file mode 100644
index 0000000..9e85236
--- /dev/null
+++ b/file_io/os2/mktemp.c
@@ -0,0 +1 @@
+#include "../unix/mktemp.c"
diff --git a/file_io/os2/open.c b/file_io/os2/open.c
new file mode 100644
index 0000000..12badd6
--- /dev/null
+++ b/file_io/os2/open.c
@@ -0,0 +1,304 @@
+/* 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_lib.h"
+#include "apr_portable.h"
+#include "apr_strings.h"
+#include "apr_arch_inherit.h"
+#include <string.h>
+
+apr_status_t apr_file_cleanup(void *thefile)
+{
+ apr_file_t *file = thefile;
+ return apr_file_close(file);
+}
+
+
+
+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)
+{
+ int oflags = 0;
+ int mflags = OPEN_FLAGS_FAIL_ON_ERROR|OPEN_SHARE_DENYNONE|OPEN_FLAGS_NOINHERIT;
+ int rv;
+ ULONG action;
+ apr_file_t *dafile = (apr_file_t *)apr_palloc(pool, sizeof(apr_file_t));
+
+ if (flag & APR_FOPEN_NONBLOCK) {
+ return APR_ENOTIMPL;
+ }
+
+ dafile->pool = pool;
+ dafile->isopen = FALSE;
+ dafile->eof_hit = FALSE;
+ dafile->buffer = NULL;
+ dafile->flags = flag;
+ dafile->blocking = BLK_ON;
+
+ if ((flag & APR_FOPEN_READ) && (flag & APR_FOPEN_WRITE)) {
+ mflags |= OPEN_ACCESS_READWRITE;
+ } else if (flag & APR_FOPEN_READ) {
+ mflags |= OPEN_ACCESS_READONLY;
+ } else if (flag & APR_FOPEN_WRITE) {
+ mflags |= OPEN_ACCESS_WRITEONLY;
+ } else {
+ dafile->filedes = -1;
+ return APR_EACCES;
+ }
+
+ dafile->buffered = (flag & APR_FOPEN_BUFFERED) > 0;
+
+ if (dafile->buffered) {
+ dafile->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
+ dafile->bufsize = APR_FILE_DEFAULT_BUFSIZE;
+ rv = apr_thread_mutex_create(&dafile->mutex, 0, pool);
+
+ if (rv)
+ return rv;
+ }
+
+ if (flag & APR_FOPEN_CREATE) {
+ oflags |= OPEN_ACTION_CREATE_IF_NEW;
+
+ if (!(flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_TRUNCATE)) {
+ oflags |= OPEN_ACTION_OPEN_IF_EXISTS;
+ }
+ }
+
+ if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE))
+ return APR_EACCES;
+
+ if (flag & APR_FOPEN_TRUNCATE) {
+ oflags |= OPEN_ACTION_REPLACE_IF_EXISTS;
+ } else if ((oflags & 0xFF) == 0) {
+ oflags |= OPEN_ACTION_OPEN_IF_EXISTS;
+ }
+
+ rv = DosOpen(fname, &(dafile->filedes), &action, 0, 0, oflags, mflags, NULL);
+
+ if (rv == 0 && (flag & APR_FOPEN_APPEND)) {
+ ULONG newptr;
+ rv = DosSetFilePtr(dafile->filedes, 0, FILE_END, &newptr );
+
+ if (rv)
+ DosClose(dafile->filedes);
+ }
+
+ if (rv != 0)
+ return APR_FROM_OS_ERROR(rv);
+
+ dafile->isopen = TRUE;
+ dafile->fname = apr_pstrdup(pool, fname);
+ dafile->filePtr = 0;
+ dafile->bufpos = 0;
+ dafile->dataRead = 0;
+ dafile->direction = 0;
+ dafile->pipe = FALSE;
+
+ if (!(flag & APR_FOPEN_NOCLEANUP)) {
+ apr_pool_cleanup_register(dafile->pool, dafile, apr_file_cleanup, apr_file_cleanup);
+ }
+
+ *new = dafile;
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
+{
+ ULONG rc;
+ apr_status_t status;
+
+ if (file && file->isopen) {
+ /* XXX: flush here is not mutex protected */
+ status = apr_file_flush(file);
+ rc = DosClose(file->filedes);
+
+ if (rc == 0) {
+ file->isopen = FALSE;
+
+ if (file->flags & APR_FOPEN_DELONCLOSE) {
+ status = APR_FROM_OS_ERROR(DosDelete(file->fname));
+ }
+ /* else we return the status of the flush attempt
+ * when all else succeeds
+ */
+ } else {
+ return APR_FROM_OS_ERROR(rc);
+ }
+ }
+
+ if (file->buffered)
+ apr_thread_mutex_destroy(file->mutex);
+
+ return status;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
+{
+ ULONG rc = DosDelete(path);
+ return APR_FROM_OS_ERROR(rc);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_rename(const char *from_path, const char *to_path,
+ apr_pool_t *p)
+{
+ ULONG rc = DosMove(from_path, to_path);
+
+ if (rc == ERROR_ACCESS_DENIED || rc == ERROR_ALREADY_EXISTS) {
+ rc = DosDelete(to_path);
+
+ if (rc == 0 || rc == ERROR_FILE_NOT_FOUND) {
+ rc = DosMove(from_path, to_path);
+ }
+ }
+
+ return APR_FROM_OS_ERROR(rc);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile, apr_file_t *file)
+{
+ *thefile = file->filedes;
+ 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)
+{
+ apr_os_file_t *dafile = thefile;
+
+ (*file) = apr_palloc(pool, sizeof(apr_file_t));
+ (*file)->pool = pool;
+ (*file)->filedes = *dafile;
+ (*file)->isopen = TRUE;
+ (*file)->eof_hit = FALSE;
+ (*file)->flags = flags;
+ (*file)->pipe = FALSE;
+ (*file)->buffered = (flags & APR_FOPEN_BUFFERED) > 0;
+
+ if ((*file)->buffered) {
+ apr_status_t rv;
+
+ (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
+ (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
+ rv = apr_thread_mutex_create(&(*file)->mutex, 0, pool);
+
+ if (rv)
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_eof(apr_file_t *fptr)
+{
+ if (!fptr->isopen || 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)
+{
+ apr_os_file_t fd = 2;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
+ apr_int32_t flags,
+ apr_pool_t *pool)
+{
+ apr_os_file_t fd = 1;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
+ apr_int32_t flags,
+ apr_pool_t *pool)
+{
+ apr_os_file_t fd = 0;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_READ, pool);
+}
+
+
+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_DECLARE(apr_status_t) apr_file_inherit_set(apr_file_t *thefile)
+{
+ int rv;
+ ULONG state;
+
+ rv = DosQueryFHState(thefile->filedes, &state);
+
+ if (rv == 0 && (state & OPEN_FLAGS_NOINHERIT) != 0) {
+ rv = DosSetFHState(thefile->filedes, state & ~OPEN_FLAGS_NOINHERIT);
+ }
+
+ return APR_FROM_OS_ERROR(rv);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_inherit_unset(apr_file_t *thefile)
+{
+ int rv;
+ ULONG state;
+
+ rv = DosQueryFHState(thefile->filedes, &state);
+
+ if (rv == 0 && (state & OPEN_FLAGS_NOINHERIT) == 0) {
+ rv = DosSetFHState(thefile->filedes, state | OPEN_FLAGS_NOINHERIT);
+ }
+
+ return APR_FROM_OS_ERROR(rv);
+}
diff --git a/file_io/os2/pipe.c b/file_io/os2/pipe.c
new file mode 100644
index 0000000..14f2b1a
--- /dev/null
+++ b/file_io/os2/pipe.c
@@ -0,0 +1,236 @@
+/* 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.
+ */
+
+#define INCL_DOSERRORS
+#include "apr_arch_file_io.h"
+#include "apr_file_io.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include <string.h>
+#include <process.h>
+
+static apr_status_t file_pipe_create(apr_file_t **in, apr_file_t **out,
+ apr_pool_t *pool_in, apr_pool_t *pool_out)
+{
+ ULONG filedes[2];
+ ULONG rc, action;
+ static int id = 0;
+ char pipename[50];
+
+ sprintf(pipename, "/pipe/%d.%d", getpid(), id++);
+ rc = DosCreateNPipe(pipename, filedes, NP_ACCESS_INBOUND, NP_NOWAIT|1, 4096, 4096, 0);
+
+ if (rc)
+ return APR_FROM_OS_ERROR(rc);
+
+ rc = DosConnectNPipe(filedes[0]);
+
+ if (rc && rc != ERROR_PIPE_NOT_CONNECTED) {
+ DosClose(filedes[0]);
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ rc = DosOpen (pipename, filedes+1, &action, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
+ OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE,
+ NULL);
+
+ if (rc) {
+ DosClose(filedes[0]);
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ (*in) = (apr_file_t *)apr_palloc(pool_in, sizeof(apr_file_t));
+ rc = DosCreateEventSem(NULL, &(*in)->pipeSem, DC_SEM_SHARED, FALSE);
+
+ if (rc) {
+ DosClose(filedes[0]);
+ DosClose(filedes[1]);
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ rc = DosSetNPipeSem(filedes[0], (HSEM)(*in)->pipeSem, 1);
+
+ if (!rc) {
+ rc = DosSetNPHState(filedes[0], NP_WAIT);
+ }
+
+ if (rc) {
+ DosClose(filedes[0]);
+ DosClose(filedes[1]);
+ DosCloseEventSem((*in)->pipeSem);
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ (*in)->pool = pool_in;
+ (*in)->filedes = filedes[0];
+ (*in)->fname = apr_pstrdup(pool_in, pipename);
+ (*in)->isopen = TRUE;
+ (*in)->buffered = FALSE;
+ (*in)->flags = 0;
+ (*in)->pipe = 1;
+ (*in)->timeout = -1;
+ (*in)->blocking = BLK_ON;
+ apr_pool_cleanup_register(pool_in, *in, apr_file_cleanup,
+ apr_pool_cleanup_null);
+
+ (*out) = (apr_file_t *)apr_palloc(pool_out, sizeof(apr_file_t));
+ (*out)->pool = pool_out;
+ (*out)->filedes = filedes[1];
+ (*out)->fname = apr_pstrdup(pool_out, pipename);
+ (*out)->isopen = TRUE;
+ (*out)->buffered = FALSE;
+ (*out)->flags = 0;
+ (*out)->pipe = 1;
+ (*out)->timeout = -1;
+ (*out)->blocking = BLK_ON;
+ apr_pool_cleanup_register(pool_out, *out, apr_file_cleanup,
+ apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+static void file_pipe_block(apr_file_t **in, apr_file_t **out,
+ apr_int32_t blocking)
+{
+ switch (blocking) {
+ case APR_FULL_BLOCK:
+ break;
+ case APR_READ_BLOCK:
+ apr_file_pipe_timeout_set(*out, 0);
+ break;
+ case APR_WRITE_BLOCK:
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ default:
+ apr_file_pipe_timeout_set(*out, 0);
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_create(apr_file_t **in,
+ apr_file_t **out,
+ apr_pool_t *pool)
+{
+ return file_pipe_create(in, out, pool, pool);
+}
+
+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 *pool)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool, pool)) != APR_SUCCESS)
+ return status;
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+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)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool_in, pool_out)) != APR_SUCCESS)
+ return status;
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+
+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 */
+ return APR_ENOTIMPL;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_set(apr_file_t *thepipe, apr_interval_time_t timeout)
+{
+ if (thepipe->pipe == 1) {
+ thepipe->timeout = timeout;
+
+ if (thepipe->timeout >= 0) {
+ if (thepipe->blocking != BLK_OFF) {
+ thepipe->blocking = BLK_OFF;
+ return APR_FROM_OS_ERROR(DosSetNPHState(thepipe->filedes, NP_NOWAIT));
+ }
+ }
+ else if (thepipe->timeout == -1) {
+ if (thepipe->blocking != BLK_ON) {
+ thepipe->blocking = BLK_ON;
+ return APR_FROM_OS_ERROR(DosSetNPHState(thepipe->filedes, NP_WAIT));
+ }
+ }
+ }
+ return APR_EINVAL;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_get(apr_file_t *thepipe, apr_interval_time_t *timeout)
+{
+ if (thepipe->pipe == 1) {
+ *timeout = thepipe->timeout;
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+
+
+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)->isopen = TRUE;
+ (*file)->pipe = 1;
+ (*file)->blocking = BLK_UNKNOWN; /* app needs to make a timeout call */
+ (*file)->timeout = -1;
+ (*file)->filedes = *thefile;
+
+ if (register_cleanup) {
+ apr_pool_cleanup_register(pool, *file, apr_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);
+}
diff --git a/file_io/os2/readwrite.c b/file_io/os2/readwrite.c
new file mode 100644
index 0000000..d00591d
--- /dev/null
+++ b/file_io/os2/readwrite.c
@@ -0,0 +1,388 @@
+/* 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.
+ */
+
+#define INCL_DOS
+#define INCL_DOSERRORS
+
+#include "apr_arch_file_io.h"
+#include "apr_file_io.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+
+#include <malloc.h>
+
+APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes)
+{
+ ULONG rc = 0;
+ ULONG bytesread;
+
+ if (!thefile->isopen) {
+ *nbytes = 0;
+ return APR_EBADF;
+ }
+
+ if (thefile->buffered) {
+ char *pos = (char *)buf;
+ ULONG blocksize;
+ ULONG size = *nbytes;
+
+ apr_thread_mutex_lock(thefile->mutex);
+
+ if (thefile->direction == 1) {
+ int rv = apr_file_flush(thefile);
+
+ if (rv != APR_SUCCESS) {
+ apr_thread_mutex_unlock(thefile->mutex);
+ return rv;
+ }
+
+ thefile->bufpos = 0;
+ thefile->direction = 0;
+ thefile->dataRead = 0;
+ }
+
+ while (rc == 0 && size > 0) {
+ if (thefile->bufpos >= thefile->dataRead) {
+ ULONG bytesread;
+ rc = DosRead(thefile->filedes, thefile->buffer,
+ thefile->bufsize, &bytesread);
+
+ if (bytesread == 0) {
+ if (rc == 0)
+ thefile->eof_hit = TRUE;
+ break;
+ }
+
+ thefile->dataRead = bytesread;
+ 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;
+ }
+
+ *nbytes = rc == 0 ? pos - (char *)buf : 0;
+ apr_thread_mutex_unlock(thefile->mutex);
+
+ if (*nbytes == 0 && rc == 0 && thefile->eof_hit) {
+ return APR_EOF;
+ }
+
+ return APR_FROM_OS_ERROR(rc);
+ } else {
+ if (thefile->pipe)
+ DosResetEventSem(thefile->pipeSem, &rc);
+
+ rc = DosRead(thefile->filedes, buf, *nbytes, &bytesread);
+
+ if (rc == ERROR_NO_DATA && thefile->timeout != 0) {
+ int rcwait = DosWaitEventSem(thefile->pipeSem, thefile->timeout >= 0 ? thefile->timeout / 1000 : SEM_INDEFINITE_WAIT);
+
+ if (rcwait == 0) {
+ rc = DosRead(thefile->filedes, buf, *nbytes, &bytesread);
+ }
+ else if (rcwait == ERROR_TIMEOUT) {
+ *nbytes = 0;
+ return APR_TIMEUP;
+ }
+ }
+
+ if (rc) {
+ *nbytes = 0;
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ *nbytes = bytesread;
+
+ if (bytesread == 0) {
+ thefile->eof_hit = TRUE;
+ return APR_EOF;
+ }
+
+ return APR_SUCCESS;
+ }
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
+{
+ ULONG rc = 0;
+ ULONG byteswritten;
+
+ if (!thefile->isopen) {
+ *nbytes = 0;
+ return APR_EBADF;
+ }
+
+ if (thefile->buffered) {
+ char *pos = (char *)buf;
+ int blocksize;
+ int size = *nbytes;
+
+ apr_thread_mutex_lock(thefile->mutex);
+
+ if ( thefile->direction == 0 ) {
+ /* Position file pointer for writing at the offset we are logically reading from */
+ ULONG offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
+ if (offset != thefile->filePtr)
+ DosSetFilePtr(thefile->filedes, offset, FILE_BEGIN, &thefile->filePtr );
+ thefile->bufpos = thefile->dataRead = 0;
+ thefile->direction = 1;
+ }
+
+ while (rc == 0 && size > 0) {
+ if (thefile->bufpos == thefile->bufsize) /* write buffer is full */
+ /* XXX bug; - rc is double-transformed os->apr below */
+ rc = 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;
+ }
+
+ apr_thread_mutex_unlock(thefile->mutex);
+ return APR_FROM_OS_ERROR(rc);
+ } else {
+ if (thefile->flags & APR_FOPEN_APPEND) {
+ FILELOCK all = { 0, 0x7fffffff };
+ ULONG newpos;
+ rc = DosSetFileLocks(thefile->filedes, NULL, &all, -1, 0);
+
+ if (rc == 0) {
+ rc = DosSetFilePtr(thefile->filedes, 0, FILE_END, &newpos);
+
+ if (rc == 0) {
+ rc = DosWrite(thefile->filedes, buf, *nbytes, &byteswritten);
+ }
+
+ DosSetFileLocks(thefile->filedes, &all, NULL, -1, 0);
+ }
+ } else {
+ rc = DosWrite(thefile->filedes, buf, *nbytes, &byteswritten);
+ }
+
+ if (rc) {
+ *nbytes = 0;
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ *nbytes = byteswritten;
+ return APR_SUCCESS;
+ }
+}
+
+
+
+#ifdef HAVE_WRITEV
+
+APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec, apr_size_t nvec, apr_size_t *nbytes)
+{
+ int bytes;
+
+ if (thefile->buffered) {
+ apr_status_t rv = apr_file_flush(thefile);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) {
+ *nbytes = 0;
+ return errno;
+ }
+ else {
+ *nbytes = bytes;
+ return APR_SUCCESS;
+ }
+}
+#endif
+
+
+
+APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
+{
+ ULONG rc;
+ ULONG byteswritten;
+
+ if (!thefile->isopen) {
+ return APR_EBADF;
+ }
+
+ rc = DosWrite(thefile->filedes, &ch, 1, &byteswritten);
+
+ if (rc) {
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ return APR_SUCCESS;
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_ungetc(char ch, apr_file_t *thefile)
+{
+ apr_off_t offset = -1;
+ return apr_file_seek(thefile, APR_CUR, &offset);
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_getc(char *ch, apr_file_t *thefile)
+{
+ ULONG rc;
+ apr_size_t bytesread;
+
+ if (!thefile->isopen) {
+ return APR_EBADF;
+ }
+
+ bytesread = 1;
+ rc = apr_file_read(thefile, ch, &bytesread);
+
+ if (rc) {
+ return rc;
+ }
+
+ if (bytesread == 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;
+
+ len = strlen(str);
+ return apr_file_write(thefile, str, &len);
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
+{
+ if (thefile->buffered) {
+ ULONG written = 0;
+ int rc = 0;
+
+ if (thefile->direction == 1 && thefile->bufpos) {
+ rc = DosWrite(thefile->filedes, thefile->buffer, thefile->bufpos, &written);
+ thefile->filePtr += written;
+
+ if (rc == 0)
+ thefile->bufpos = 0;
+ }
+
+ return APR_FROM_OS_ERROR(rc);
+ } else {
+ /* 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)
+{
+ return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile)
+{
+ return APR_ENOTIMPL;
+}
+
+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) {
+ break;
+ }
+
+ if (readlen != 1) {
+ rv = APR_EOF;
+ break;
+ }
+
+ if (str[i] == '\n') {
+ i++;
+ break;
+ }
+ }
+ str[i] = 0;
+ if (i > 0) {
+ /* we stored chars; don't report EOF or any other errors;
+ * the app will find out about that on the next call
+ */
+ return APR_SUCCESS;
+ }
+ return rv;
+}
+
+
+
+APR_DECLARE_NONSTD(int) apr_file_printf(apr_file_t *fptr,
+ const char *format, ...)
+{
+ int cc;
+ va_list ap;
+ char *buf;
+ int len;
+
+ buf = malloc(HUGE_STRING_LEN);
+ if (buf == NULL) {
+ return 0;
+ }
+ va_start(ap, format);
+ len = apr_vsnprintf(buf, HUGE_STRING_LEN, format, ap);
+ cc = apr_file_puts(buf, fptr);
+ va_end(ap);
+ free(buf);
+ return (cc == APR_SUCCESS) ? len : -1;
+}
+
+
+
+apr_status_t apr_file_check_read(apr_file_t *fd)
+{
+ int rc;
+
+ if (!fd->pipe)
+ return APR_SUCCESS; /* Not a pipe, assume no waiting */
+
+ rc = DosWaitEventSem(fd->pipeSem, SEM_IMMEDIATE_RETURN);
+
+ if (rc == ERROR_TIMEOUT)
+ return APR_TIMEUP;
+
+ return APR_FROM_OS_ERROR(rc);
+}
diff --git a/file_io/os2/seek.c b/file_io/os2/seek.c
new file mode 100644
index 0000000..a8d13fe
--- /dev/null
+++ b/file_io/os2/seek.c
@@ -0,0 +1,120 @@
+/* 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_lib.h"
+#include <string.h>
+#include <io.h>
+
+
+static apr_status_t setptr(apr_file_t *thefile, unsigned long pos )
+{
+ long newbufpos;
+ ULONG rc;
+
+ if (thefile->direction == 1) {
+ /* XXX: flush here is not mutex protected */
+ apr_status_t rv = apr_file_flush(thefile);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ thefile->bufpos = thefile->direction = thefile->dataRead = 0;
+ }
+
+ newbufpos = pos - (thefile->filePtr - thefile->dataRead);
+ if (newbufpos >= 0 && newbufpos <= thefile->dataRead) {
+ thefile->bufpos = newbufpos;
+ rc = 0;
+ } else {
+ rc = DosSetFilePtr(thefile->filedes, pos, FILE_BEGIN, &thefile->filePtr );
+
+ if ( !rc )
+ thefile->bufpos = thefile->dataRead = 0;
+ }
+
+ return APR_FROM_OS_ERROR(rc);
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_seek(apr_file_t *thefile, apr_seek_where_t where, apr_off_t *offset)
+{
+ if (!thefile->isopen) {
+ return APR_EBADF;
+ }
+
+ thefile->eof_hit = 0;
+
+ if (thefile->buffered) {
+ int rc = EINVAL;
+ apr_finfo_t finfo;
+
+ 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_NORM, thefile);
+ if (rc == APR_SUCCESS)
+ rc = setptr(thefile, finfo.size + *offset);
+ break;
+ }
+
+ *offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
+ return rc;
+ } else {
+ switch (where) {
+ case APR_SET:
+ where = FILE_BEGIN;
+ break;
+
+ case APR_CUR:
+ where = FILE_CURRENT;
+ break;
+
+ case APR_END:
+ where = FILE_END;
+ break;
+ }
+
+ return APR_FROM_OS_ERROR(DosSetFilePtr(thefile->filedes, *offset, where, (ULONG *)offset));
+ }
+}
+
+
+
+APR_DECLARE(apr_status_t) apr_file_trunc(apr_file_t *fp, apr_off_t offset)
+{
+ int rc = DosSetFileSize(fp->filedes, offset);
+
+ if (rc != 0) {
+ return APR_FROM_OS_ERROR(rc);
+ }
+
+ if (fp->buffered) {
+ return setptr(fp, offset);
+ }
+
+ return APR_SUCCESS;
+}
diff --git a/file_io/os2/tempdir.c b/file_io/os2/tempdir.c
new file mode 100644
index 0000000..6823569
--- /dev/null
+++ b/file_io/os2/tempdir.c
@@ -0,0 +1 @@
+#include "../unix/tempdir.c"
diff --git a/file_io/unix/buffer.c b/file_io/unix/buffer.c
new file mode 100644
index 0000000..ba2a8a7
--- /dev/null
+++ b/file_io/unix/buffer.c
@@ -0,0 +1,60 @@
+/* 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_pools.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;
+
+ file_lock(file);
+
+ if(file->buffered) {
+ /* Flush the existing buffer */
+ rv = apr_file_flush_locked(file);
+ if (rv != APR_SUCCESS) {
+ file_unlock(file);
+ 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;
+ }
+
+ file_unlock(file);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_size_t) apr_file_buffer_size_get(apr_file_t *file)
+{
+ return file->bufsize;
+}
diff --git a/file_io/unix/copy.c b/file_io/unix/copy.c
new file mode 100644
index 0000000..7f74d30
--- /dev/null
+++ b/file_io/unix/copy.c
@@ -0,0 +1,119 @@
+/* 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"
+
+static apr_status_t apr_file_transfer_contents(const char *from_path,
+ const char *to_path,
+ apr_int32_t flags,
+ apr_fileperms_t to_perms,
+ apr_pool_t *pool)
+{
+ apr_file_t *s, *d;
+ apr_status_t status;
+ apr_finfo_t finfo;
+ apr_fileperms_t perms;
+
+ /* Open source file. */
+ status = apr_file_open(&s, from_path, APR_FOPEN_READ, APR_OS_DEFAULT, pool);
+ if (status)
+ return status;
+
+ /* Maybe get its permissions. */
+ if (to_perms == APR_FILE_SOURCE_PERMS) {
+ status = apr_file_info_get(&finfo, APR_FINFO_PROT, s);
+ if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
+ apr_file_close(s); /* toss any error */
+ return status;
+ }
+ perms = finfo.protection;
+ apr_file_perms_set(to_path, perms); /* ignore any failure */
+ }
+ else
+ perms = to_perms;
+
+ /* Open dest file. */
+ status = apr_file_open(&d, to_path, flags, perms, pool);
+ if (status) {
+ apr_file_close(s); /* toss any error */
+ return status;
+ }
+
+#if BUFSIZ > APR_FILE_DEFAULT_BUFSIZE
+#define COPY_BUFSIZ BUFSIZ
+#else
+#define COPY_BUFSIZ APR_FILE_DEFAULT_BUFSIZE
+#endif
+
+ /* Copy bytes till the cows come home. */
+ while (1) {
+ char buf[COPY_BUFSIZ];
+ apr_size_t bytes_this_time = sizeof(buf);
+ apr_status_t read_err;
+ apr_status_t write_err;
+
+ /* Read 'em. */
+ read_err = apr_file_read(s, buf, &bytes_this_time);
+ if (read_err && !APR_STATUS_IS_EOF(read_err)) {
+ apr_file_close(s); /* toss any error */
+ apr_file_close(d); /* toss any error */
+ return read_err;
+ }
+
+ /* Write 'em. */
+ write_err = apr_file_write_full(d, buf, bytes_this_time, NULL);
+ if (write_err) {
+ apr_file_close(s); /* toss any error */
+ apr_file_close(d); /* toss any error */
+ return write_err;
+ }
+
+ if (read_err && APR_STATUS_IS_EOF(read_err)) {
+ status = apr_file_close(s);
+ if (status) {
+ apr_file_close(d); /* toss any error */
+ return status;
+ }
+
+ /* return the results of this close: an error, or success */
+ return apr_file_close(d);
+ }
+ }
+ /* NOTREACHED */
+}
+
+APR_DECLARE(apr_status_t) apr_file_copy(const char *from_path,
+ const char *to_path,
+ apr_fileperms_t perms,
+ apr_pool_t *pool)
+{
+ return apr_file_transfer_contents(from_path, to_path,
+ (APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE),
+ perms,
+ pool);
+}
+
+APR_DECLARE(apr_status_t) apr_file_append(const char *from_path,
+ const char *to_path,
+ apr_fileperms_t perms,
+ apr_pool_t *pool)
+{
+ return apr_file_transfer_contents(from_path, to_path,
+ (APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_APPEND),
+ perms,
+ pool);
+}
diff --git a/file_io/unix/dir.c b/file_io/unix/dir.c
new file mode 100644
index 0000000..d9b344f
--- /dev/null
+++ b/file_io/unix/dir.c
@@ -0,0 +1,373 @@
+/* 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_strings.h"
+#include "apr_portable.h"
+#if APR_HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+#if APR_HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+static apr_status_t dir_cleanup(void *thedir)
+{
+ apr_dir_t *dir = thedir;
+ if (closedir(dir->dirstruct) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+#define PATH_SEPARATOR '/'
+
+/* Remove trailing separators that don't affect the meaning of PATH. */
+static const char *path_canonicalize (const char *path, apr_pool_t *pool)
+{
+ /* At some point this could eliminate redundant components. For
+ * now, it just makes sure there is no trailing slash. */
+ apr_size_t len = strlen (path);
+ apr_size_t orig_len = len;
+
+ while ((len > 0) && (path[len - 1] == PATH_SEPARATOR))
+ len--;
+
+ if (len != orig_len)
+ return apr_pstrndup (pool, path, len);
+ else
+ return path;
+}
+
+/* Remove one component off the end of PATH. */
+static char *path_remove_last_component (const char *path, apr_pool_t *pool)
+{
+ const char *newpath = path_canonicalize (path, pool);
+ int i;
+
+ for (i = (strlen(newpath) - 1); i >= 0; i--) {
+ if (path[i] == PATH_SEPARATOR)
+ break;
+ }
+
+ return apr_pstrndup (pool, path, (i < 0) ? 0 : i);
+}
+
+apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname,
+ apr_pool_t *pool)
+{
+ DIR *dir = opendir(dirname);
+
+ if (!dir) {
+ return errno;
+ }
+
+ (*new) = (apr_dir_t *)apr_palloc(pool, sizeof(apr_dir_t));
+
+ (*new)->pool = pool;
+ (*new)->dirname = apr_pstrdup(pool, dirname);
+ (*new)->dirstruct = dir;
+
+#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
+ && !defined(READDIR_IS_THREAD_SAFE)
+ /* On some platforms (e.g., Linux+GNU libc), d_name[] in struct
+ * dirent is declared with enough storage for the name. On other
+ * platforms (e.g., Solaris 8 for Intel), d_name is declared as a
+ * one-byte array. Note: gcc evaluates this at compile time.
+ */
+ (*new)->entry = apr_pcalloc(pool, sizeof(*(*new)->entry) +
+ (sizeof((*new)->entry->d_name) > 1
+ ? 0 : NAME_MAX));
+#else
+ (*new)->entry = NULL;
+#endif
+
+ apr_pool_cleanup_register((*new)->pool, *new, dir_cleanup,
+ apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_dir_close(apr_dir_t *thedir)
+{
+ return apr_pool_cleanup_run(thedir->pool, thedir, dir_cleanup);
+}
+
+#ifdef DIRENT_TYPE
+static apr_filetype_e filetype_from_dirent_type(int type)
+{
+ switch (type) {
+ case DT_REG:
+ return APR_REG;
+ case DT_DIR:
+ return APR_DIR;
+ case DT_LNK:
+ return APR_LNK;
+ case DT_CHR:
+ return APR_CHR;
+ case DT_BLK:
+ return APR_BLK;
+#if defined(DT_FIFO)
+ case DT_FIFO:
+ return APR_PIPE;
+#endif
+#if !defined(BEOS) && defined(DT_SOCK)
+ case DT_SOCK:
+ return APR_SOCK;
+#endif
+ default:
+ return APR_UNKFILE;
+ }
+}
+#endif
+
+apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
+ apr_dir_t *thedir)
+{
+ apr_status_t ret = 0;
+#ifdef DIRENT_TYPE
+ apr_filetype_e type;
+#endif
+#if APR_HAS_THREADS && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \
+ && !defined(READDIR_IS_THREAD_SAFE)
+#ifdef APR_USE_READDIR64_R
+ struct dirent64 *retent;
+
+ /* If LFS is enabled and readdir64_r is available, readdir64_r is
+ * used in preference to readdir_r. This allows directories to be
+ * read which contain a (64-bit) inode number which doesn't fit
+ * into the 32-bit apr_ino_t, iff the caller doesn't actually care
+ * about the inode number (i.e. wanted & APR_FINFO_INODE == 0).
+ * (such inodes may be seen in some wonky NFS environments)
+ *
+ * Similarly, if the d_off field cannot be reprented in a 32-bit
+ * offset, the libc readdir_r() would barf; using readdir64_r
+ * bypasses that case entirely since APR does not care about
+ * d_off. */
+
+ ret = readdir64_r(thedir->dirstruct, thedir->entry, &retent);
+#else
+
+ struct dirent *retent;
+
+ ret = readdir_r(thedir->dirstruct, thedir->entry, &retent);
+#endif
+
+ /* POSIX treats "end of directory" as a non-error case, so ret
+ * will be zero and retent will be set to NULL in that case. */
+ if (!ret && retent == NULL) {
+ ret = APR_ENOENT;
+ }
+
+ /* Solaris is a bit strange, if there are no more entries in the
+ * directory, it returns EINVAL. Since this is against POSIX, we
+ * hack around the problem here. EINVAL is possible from other
+ * readdir implementations, but only if the result buffer is too small.
+ * since we control the size of that buffer, we should never have
+ * that problem.
+ */
+ if (ret == EINVAL) {
+ ret = APR_ENOENT;
+ }
+#else
+ /* We're about to call a non-thread-safe readdir() that may
+ possibly set `errno', and the logic below actually cares about
+ errno after the call. Therefore we need to clear errno first. */
+ errno = 0;
+ thedir->entry = readdir(thedir->dirstruct);
+ if (thedir->entry == NULL) {
+ /* If NULL was returned, this can NEVER be a success. Can it?! */
+ if (errno == APR_SUCCESS) {
+ ret = APR_ENOENT;
+ }
+ else
+ ret = errno;
+ }
+#endif
+
+ /* No valid bit flag to test here - do we want one? */
+ finfo->fname = NULL;
+
+ if (ret) {
+ finfo->valid = 0;
+ return ret;
+ }
+
+#ifdef DIRENT_TYPE
+ type = filetype_from_dirent_type(thedir->entry->DIRENT_TYPE);
+ if (type != APR_UNKFILE) {
+ wanted &= ~APR_FINFO_TYPE;
+ }
+#endif
+#ifdef DIRENT_INODE
+ if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
+#ifdef APR_USE_READDIR64_R
+ /* If readdir64_r is used, check for the overflow case of trying
+ * to fit a 64-bit integer into a 32-bit integer. */
+ if (sizeof(apr_ino_t) >= sizeof(retent->DIRENT_INODE)
+ || (apr_ino_t)retent->DIRENT_INODE == retent->DIRENT_INODE) {
+ wanted &= ~APR_FINFO_INODE;
+ } else {
+ /* Prevent the fallback code below from filling in the
+ * inode if the stat call fails. */
+ retent->DIRENT_INODE = 0;
+ }
+#else
+ wanted &= ~APR_FINFO_INODE;
+#endif /* APR_USE_READDIR64_R */
+ }
+#endif /* DIRENT_INODE */
+
+ wanted &= ~APR_FINFO_NAME;
+
+ if (wanted)
+ {
+ char fspec[APR_PATH_MAX];
+ char *end;
+
+ end = apr_cpystrn(fspec, thedir->dirname, sizeof fspec);
+
+ if (end > fspec && end[-1] != '/' && (end < fspec + APR_PATH_MAX))
+ *end++ = '/';
+
+ apr_cpystrn(end, thedir->entry->d_name,
+ sizeof fspec - (end - fspec));
+
+ ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
+ /* We passed a stack name that will disappear */
+ finfo->fname = NULL;
+ }
+
+ if (wanted && (ret == APR_SUCCESS || ret == APR_INCOMPLETE)) {
+ wanted &= ~finfo->valid;
+ }
+ else {
+ /* We don't bail because we fail to stat, when we are only -required-
+ * to readdir... but the result will be APR_INCOMPLETE
+ */
+ finfo->pool = thedir->pool;
+ finfo->valid = 0;
+#ifdef DIRENT_TYPE
+ if (type != APR_UNKFILE) {
+ finfo->filetype = type;
+ finfo->valid |= APR_FINFO_TYPE;
+ }
+#endif
+#ifdef DIRENT_INODE
+ if (thedir->entry->DIRENT_INODE && thedir->entry->DIRENT_INODE != -1) {
+ finfo->inode = thedir->entry->DIRENT_INODE;
+ finfo->valid |= APR_FINFO_INODE;
+ }
+#endif
+ }
+
+ finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
+ finfo->valid |= APR_FINFO_NAME;
+
+ if (wanted)
+ return APR_INCOMPLETE;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_dir_rewind(apr_dir_t *thedir)
+{
+ rewinddir(thedir->dirstruct);
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_dir_make(const char *path, apr_fileperms_t perm,
+ apr_pool_t *pool)
+{
+ mode_t mode = apr_unix_perms2mode(perm);
+
+ if (mkdir(path, mode) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+apr_status_t apr_dir_make_recursive(const char *path, apr_fileperms_t perm,
+ apr_pool_t *pool)
+{
+ apr_status_t apr_err = 0;
+
+ apr_err = apr_dir_make (path, perm, pool); /* Try to make PATH right out */
+
+ if (apr_err == ENOENT) { /* Missing an intermediate dir */
+ char *dir;
+
+ dir = path_remove_last_component(path, pool);
+ /* If there is no path left, give up. */
+ if (dir[0] == '\0') {
+ return apr_err;
+ }
+
+ apr_err = apr_dir_make_recursive(dir, perm, pool);
+
+ if (!apr_err)
+ apr_err = apr_dir_make (path, perm, pool);
+ }
+
+ /*
+ * 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.
+ */
+ if (APR_STATUS_IS_EEXIST(apr_err))
+ return APR_SUCCESS;
+
+ return apr_err;
+}
+
+apr_status_t apr_dir_remove(const char *path, apr_pool_t *pool)
+{
+ if (rmdir(path) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+apr_status_t apr_os_dir_get(apr_os_dir_t **thedir, apr_dir_t *dir)
+{
+ if (dir == NULL) {
+ return APR_ENODIR;
+ }
+ *thedir = dir->dirstruct;
+ return APR_SUCCESS;
+}
+
+apr_status_t apr_os_dir_put(apr_dir_t **dir, apr_os_dir_t *thedir,
+ apr_pool_t *pool)
+{
+ if ((*dir) == NULL) {
+ (*dir) = (apr_dir_t *)apr_pcalloc(pool, sizeof(apr_dir_t));
+ (*dir)->pool = pool;
+ }
+ (*dir)->dirstruct = thedir;
+ return APR_SUCCESS;
+}
+
+
diff --git a/file_io/unix/fileacc.c b/file_io/unix/fileacc.c
new file mode 100644
index 0000000..437f358
--- /dev/null
+++ b/file_io/unix/fileacc.c
@@ -0,0 +1,119 @@
+/* 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_strings.h"
+#include "apr_arch_file_io.h"
+
+/* A file to put ALL of the accessor functions for apr_file_t types. */
+
+APR_DECLARE(apr_status_t) apr_file_name_get(const char **fname,
+ apr_file_t *thefile)
+{
+ *fname = thefile->fname;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_int32_t) apr_file_flags_get(apr_file_t *f)
+{
+ return f->flags;
+}
+
+#if !defined(OS2) && !defined(WIN32)
+mode_t apr_unix_perms2mode(apr_fileperms_t perms)
+{
+ mode_t mode = 0;
+
+ if (perms & APR_USETID)
+ mode |= S_ISUID;
+ if (perms & APR_UREAD)
+ mode |= S_IRUSR;
+ if (perms & APR_UWRITE)
+ mode |= S_IWUSR;
+ if (perms & APR_UEXECUTE)
+ mode |= S_IXUSR;
+
+ if (perms & APR_GSETID)
+ mode |= S_ISGID;
+ if (perms & APR_GREAD)
+ mode |= S_IRGRP;
+ if (perms & APR_GWRITE)
+ mode |= S_IWGRP;
+ if (perms & APR_GEXECUTE)
+ mode |= S_IXGRP;
+
+#ifdef S_ISVTX
+ if (perms & APR_WSTICKY)
+ mode |= S_ISVTX;
+#endif
+ if (perms & APR_WREAD)
+ mode |= S_IROTH;
+ if (perms & APR_WWRITE)
+ mode |= S_IWOTH;
+ if (perms & APR_WEXECUTE)
+ mode |= S_IXOTH;
+
+ return mode;
+}
+
+apr_fileperms_t apr_unix_mode2perms(mode_t mode)
+{
+ apr_fileperms_t perms = 0;
+
+ if (mode & S_ISUID)
+ perms |= APR_USETID;
+ if (mode & S_IRUSR)
+ perms |= APR_UREAD;
+ if (mode & S_IWUSR)
+ perms |= APR_UWRITE;
+ if (mode & S_IXUSR)
+ perms |= APR_UEXECUTE;
+
+ if (mode & S_ISGID)
+ perms |= APR_GSETID;
+ if (mode & S_IRGRP)
+ perms |= APR_GREAD;
+ if (mode & S_IWGRP)
+ perms |= APR_GWRITE;
+ if (mode & S_IXGRP)
+ perms |= APR_GEXECUTE;
+
+#ifdef S_ISVTX
+ if (mode & S_ISVTX)
+ perms |= APR_WSTICKY;
+#endif
+ if (mode & S_IROTH)
+ perms |= APR_WREAD;
+ if (mode & S_IWOTH)
+ perms |= APR_WWRITE;
+ if (mode & S_IXOTH)
+ perms |= APR_WEXECUTE;
+
+ return perms;
+}
+#endif
+
+APR_DECLARE(apr_status_t) apr_file_data_get(void **data, const char *key,
+ apr_file_t *file)
+{
+ return apr_pool_userdata_get(data, key, file->pool);
+}
+
+APR_DECLARE(apr_status_t) apr_file_data_set(apr_file_t *file, void *data,
+ const char *key,
+ apr_status_t (*cleanup)(void *))
+{
+ return apr_pool_userdata_set(data, key, cleanup, file->pool);
+}
diff --git a/file_io/unix/filedup.c b/file_io/unix/filedup.c
new file mode 100644
index 0000000..97d11b6
--- /dev/null
+++ b/file_io/unix/filedup.c
@@ -0,0 +1,181 @@
+/* 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_strings.h"
+#include "apr_portable.h"
+#include "apr_thread_mutex.h"
+#include "apr_arch_inherit.h"
+
+static apr_status_t file_dup(apr_file_t **new_file,
+ apr_file_t *old_file, apr_pool_t *p,
+ int which_dup)
+{
+ int rv;
+#ifdef HAVE_DUP3
+ int flags = 0;
+#endif
+
+ if (which_dup == 2) {
+ if ((*new_file) == NULL) {
+ /* We can't dup2 unless we have a valid new_file */
+ return APR_EINVAL;
+ }
+#ifdef HAVE_DUP3
+ if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT)))
+ flags |= O_CLOEXEC;
+ rv = dup3(old_file->filedes, (*new_file)->filedes, flags);
+#else
+ rv = dup2(old_file->filedes, (*new_file)->filedes);
+ if (!((*new_file)->flags & (APR_FOPEN_NOCLEANUP|APR_INHERIT))) {
+ int flags;
+
+ if (rv == -1)
+ return errno;
+
+ if ((flags = fcntl((*new_file)->filedes, F_GETFD)) == -1)
+ return errno;
+
+ flags |= FD_CLOEXEC;
+ if (fcntl((*new_file)->filedes, F_SETFD, flags) == -1)
+ return errno;
+
+ }
+#endif
+ } else {
+ rv = dup(old_file->filedes);
+ }
+
+ if (rv == -1)
+ return errno;
+
+ if (which_dup == 1) {
+ (*new_file) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t));
+ (*new_file)->pool = p;
+ (*new_file)->filedes = rv;
+ }
+
+ (*new_file)->fname = apr_pstrdup(p, old_file->fname);
+ (*new_file)->buffered = old_file->buffered;
+
+ /* If the existing socket in a dup2 is already buffered, we
+ * have an existing and valid (hopefully) mutex, so we don't
+ * want to create it again as we could leak!
+ */
+#if APR_HAS_THREADS
+ if ((*new_file)->buffered && !(*new_file)->thlock && old_file->thlock) {
+ apr_thread_mutex_create(&((*new_file)->thlock),
+ APR_THREAD_MUTEX_DEFAULT, p);
+ }
+#endif
+ /* As above, only create the buffer if we haven't already
+ * got one.
+ */
+ if ((*new_file)->buffered && !(*new_file)->buffer) {
+ (*new_file)->buffer = apr_palloc(p, old_file->bufsize);
+ (*new_file)->bufsize = old_file->bufsize;
+ }
+
+ /* this is the way dup() works */
+ (*new_file)->blocking = old_file->blocking;
+
+ /* make sure unget behavior is consistent */
+ (*new_file)->ungetchar = old_file->ungetchar;
+
+ /* apr_file_dup2() retains the original cleanup, reflecting
+ * the existing inherit and nocleanup flags. This means,
+ * that apr_file_dup2() cannot be called against an apr_file_t
+ * already closed with apr_file_close, because the expected
+ * cleanup was already killed.
+ */
+ if (which_dup == 2) {
+ return APR_SUCCESS;
+ }
+
+ /* apr_file_dup() retains all old_file flags with the exceptions
+ * of APR_INHERIT and APR_FOPEN_NOCLEANUP.
+ * The user must call apr_file_inherit_set() on the dupped
+ * apr_file_t when desired.
+ */
+ (*new_file)->flags = old_file->flags
+ & ~(APR_INHERIT | APR_FOPEN_NOCLEANUP);
+
+ apr_pool_cleanup_register((*new_file)->pool, (void *)(*new_file),
+ apr_unix_file_cleanup,
+ apr_unix_child_file_cleanup);
+#ifndef WAITIO_USES_POLL
+ /* Start out with no pollset. apr_wait_for_io_or_timeout() will
+ * initialize the pollset if needed.
+ */
+ (*new_file)->pollset = NULL;
+#endif
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_dup(apr_file_t **new_file,
+ apr_file_t *old_file, apr_pool_t *p)
+{
+ return file_dup(new_file, old_file, p, 1);
+}
+
+APR_DECLARE(apr_status_t) apr_file_dup2(apr_file_t *new_file,
+ apr_file_t *old_file, apr_pool_t *p)
+{
+ return file_dup(&new_file, old_file, p, 2);
+}
+
+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 APR_HAS_THREADS
+ if (old_file->thlock) {
+ apr_thread_mutex_create(&((*new_file)->thlock),
+ APR_THREAD_MUTEX_DEFAULT, p);
+ apr_thread_mutex_destroy(old_file->thlock);
+ }
+#endif /* APR_HAS_THREADS */
+ }
+ if (old_file->fname) {
+ (*new_file)->fname = apr_pstrdup(p, old_file->fname);
+ }
+ if (!(old_file->flags & APR_FOPEN_NOCLEANUP)) {
+ apr_pool_cleanup_kill(old_file->pool, (void *)old_file,
+ apr_unix_file_cleanup);
+ apr_pool_cleanup_register(p, (void *)(*new_file),
+ apr_unix_file_cleanup,
+ ((*new_file)->flags & APR_INHERIT)
+ ? apr_pool_cleanup_null
+ : apr_unix_child_file_cleanup);
+ }
+
+ old_file->filedes = -1;
+#ifndef WAITIO_USES_POLL
+ (*new_file)->pollset = NULL;
+#endif
+ return APR_SUCCESS;
+}
diff --git a/file_io/unix/filepath.c b/file_io/unix/filepath.c
new file mode 100644
index 0000000..6a65b20
--- /dev/null
+++ b/file_io/unix/filepath.c
@@ -0,0 +1,314 @@
+/* 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_private.h"
+#include "apr_arch_file_io.h"
+#include "apr_file_io.h"
+#include "apr_strings.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* Win32 malpropism that can go away once everyone believes this
+ * code is golden, and I'm not testing it anymore :-)
+ */
+#if APR_HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+/* Any OS that requires/refuses trailing slashes should be dealt with here.
+ */
+APR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char path[APR_PATH_MAX];
+
+ if (!getcwd(path, sizeof(path))) {
+ if (errno == ERANGE)
+ return APR_ENAMETOOLONG;
+ else
+ return errno;
+ }
+ *defpath = apr_pstrdup(p, path);
+
+ return APR_SUCCESS;
+}
+
+
+/* Any OS that requires/refuses trailing slashes should be dealt with here
+ */
+APR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
+{
+ if (chdir(path) != 0)
+ return errno;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
+ const char **inpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ if (**inpath == '/') {
+ *rootpath = apr_pstrdup(p, "/");
+ do {
+ ++(*inpath);
+ } while (**inpath == '/');
+
+ return APR_SUCCESS;
+ }
+
+ return APR_ERELATIVE;
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
+ const char *rootpath,
+ const char *addpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char *path;
+ apr_size_t rootlen; /* is the length of the src rootpath */
+ apr_size_t maxlen; /* maximum total path length */
+ apr_size_t keptlen; /* is the length of the retained rootpath */
+ apr_size_t pathlen; /* is the length of the result path */
+ apr_size_t seglen; /* is the end of the current segment */
+ apr_status_t rv;
+
+ /* Treat null as an empty path.
+ */
+ if (!addpath)
+ addpath = "";
+
+ if (addpath[0] == '/') {
+ /* If addpath is rooted, then rootpath is unused.
+ * Ths violates any APR_FILEPATH_SECUREROOTTEST and
+ * APR_FILEPATH_NOTABSOLUTE flags specified.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+ if (flags & APR_FILEPATH_NOTABSOLUTE)
+ return APR_EABSOLUTE;
+
+ /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
+ * we won't test the root again, it's ignored.
+ * Waste no CPU retrieving the working path.
+ */
+ if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
+ rootpath = "";
+ }
+ else {
+ /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
+ * requires a relative result. If the rootpath is
+ * ommitted, we do not retrieve the working path,
+ * if rootpath was supplied as absolute then fail.
+ */
+ if (flags & APR_FILEPATH_NOTABSOLUTE) {
+ if (!rootpath)
+ rootpath = "";
+ else if (rootpath[0] == '/')
+ return APR_EABSOLUTE;
+ }
+ }
+
+ if (!rootpath) {
+ /* Start with the current working path. This is bass akwards,
+ * but required since the compiler (at least vc) doesn't like
+ * passing the address of a char const* for a char** arg.
+ */
+ char *getpath;
+ rv = apr_filepath_get(&getpath, flags, p);
+ rootpath = getpath;
+ if (rv != APR_SUCCESS)
+ return errno;
+
+ /* XXX: Any kernel subject to goofy, uncanonical results
+ * must run the rootpath against the user's given flags.
+ * Simplest would be a recursive call to apr_filepath_merge
+ * with an empty (not null) rootpath and addpath of the cwd.
+ */
+ }
+
+ rootlen = strlen(rootpath);
+ maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
+ * root, and at end, plus trailing
+ * null */
+ if (maxlen > APR_PATH_MAX) {
+ return APR_ENAMETOOLONG;
+ }
+ path = (char *)apr_palloc(p, maxlen);
+
+ if (addpath[0] == '/') {
+ /* Ignore the given root path, strip off leading
+ * '/'s to a single leading '/' from the addpath,
+ * and leave addpath at the first non-'/' character.
+ */
+ keptlen = 0;
+ while (addpath[0] == '/')
+ ++addpath;
+ path[0] = '/';
+ pathlen = 1;
+ }
+ else {
+ /* If both paths are relative, fail early
+ */
+ if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
+ return APR_ERELATIVE;
+
+ /* Base the result path on the rootpath
+ */
+ keptlen = rootlen;
+ memcpy(path, rootpath, rootlen);
+
+ /* Always '/' terminate the given root path
+ */
+ if (keptlen && path[keptlen - 1] != '/') {
+ path[keptlen++] = '/';
+ }
+ pathlen = keptlen;
+ }
+
+ while (*addpath) {
+ /* Parse each segment, find the closing '/'
+ */
+ const char *next = addpath;
+ while (*next && (*next != '/')) {
+ ++next;
+ }
+ seglen = next - addpath;
+
+ if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
+ /* noop segment (/ or ./) so skip it
+ */
+ }
+ else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
+ /* backpath (../) */
+ if (pathlen == 1 && path[0] == '/') {
+ /* Attempt to move above root. Always die if the
+ * APR_FILEPATH_SECUREROOTTEST flag is specified.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST) {
+ return APR_EABOVEROOT;
+ }
+
+ /* Otherwise this is simply a noop, above root is root.
+ * Flag that rootpath was entirely replaced.
+ */
+ keptlen = 0;
+ }
+ else if (pathlen == 0
+ || (pathlen == 3
+ && !memcmp(path + pathlen - 3, "../", 3))
+ || (pathlen > 3
+ && !memcmp(path + pathlen - 4, "/../", 4))) {
+ /* Path is already backpathed or empty, if the
+ * APR_FILEPATH_SECUREROOTTEST.was given die now.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST) {
+ return APR_EABOVEROOT;
+ }
+
+ /* Otherwise append another backpath, including
+ * trailing slash if present.
+ */
+ memcpy(path + pathlen, "../", *next ? 3 : 2);
+ pathlen += *next ? 3 : 2;
+ }
+ else {
+ /* otherwise crop the prior segment
+ */
+ do {
+ --pathlen;
+ } while (pathlen && path[pathlen - 1] != '/');
+ }
+
+ /* Now test if we are above where we started and back up
+ * the keptlen offset to reflect the added/altered path.
+ */
+ if (pathlen < keptlen) {
+ if (flags & APR_FILEPATH_SECUREROOTTEST) {
+ return APR_EABOVEROOT;
+ }
+ keptlen = pathlen;
+ }
+ }
+ else {
+ /* An actual segment, append it to the destination path
+ */
+ if (*next) {
+ seglen++;
+ }
+ memcpy(path + pathlen, addpath, seglen);
+ pathlen += seglen;
+ }
+
+ /* Skip over trailing slash to the next segment
+ */
+ if (*next) {
+ ++next;
+ }
+
+ addpath = next;
+ }
+ path[pathlen] = '\0';
+
+ /* keptlen will be the rootlen unless the addpath contained
+ * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
+ * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
+ * compare the original root to assure the result path is
+ * still within given root path.
+ */
+ if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
+ if (strncmp(rootpath, path, rootlen)) {
+ return APR_EABOVEROOT;
+ }
+ if (rootpath[rootlen - 1] != '/'
+ && path[rootlen] && path[rootlen] != '/') {
+ return APR_EABOVEROOT;
+ }
+ }
+
+ *newpath = path;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
+ const char *liststr,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_split_impl(pathelts, liststr, ':', p);
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
+ apr_array_header_t *pathelts,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_merge_impl(liststr, pathelts, ':', p);
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
+{
+#if defined(DARWIN)
+ *style = APR_FILEPATH_ENCODING_UTF8;
+#else
+ *style = APR_FILEPATH_ENCODING_LOCALE;
+#endif
+ return APR_SUCCESS;
+}
diff --git a/file_io/unix/filepath_util.c b/file_io/unix/filepath_util.c
new file mode 100644
index 0000000..d8ccc56
--- /dev/null
+++ b/file_io/unix/filepath_util.c
@@ -0,0 +1,111 @@
+/* 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.
+ */
+
+
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+#include "apr_errno.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_tables.h"
+
+#include "apr_private.h"
+
+apr_status_t apr_filepath_list_split_impl(apr_array_header_t **pathelts,
+ const char *liststr,
+ char separator,
+ apr_pool_t *p)
+{
+ char *path, *part, *ptr;
+ char separator_string[2] = { '\0', '\0' };
+ apr_array_header_t *elts;
+ int nelts;
+
+ separator_string[0] = separator;
+ /* Count the number of path elements. We know there'll be at least
+ one even if path is an empty string. */
+ path = apr_pstrdup(p, liststr);
+ for (nelts = 0, ptr = path; ptr != NULL; ++nelts)
+ {
+ ptr = strchr(ptr, separator);
+ if (ptr)
+ ++ptr;
+ }
+
+ /* Split the path into the array. */
+ elts = apr_array_make(p, nelts, sizeof(char*));
+ while ((part = apr_strtok(path, separator_string, &ptr)) != NULL)
+ {
+ if (*part == '\0') /* Ignore empty path components. */
+ continue;
+
+ *(char**)apr_array_push(elts) = part;
+ path = NULL; /* For the next call to apr_strtok */
+ }
+
+ *pathelts = elts;
+ return APR_SUCCESS;
+}
+
+
+apr_status_t apr_filepath_list_merge_impl(char **liststr,
+ apr_array_header_t *pathelts,
+ char separator,
+ apr_pool_t *p)
+{
+ apr_size_t path_size = 0;
+ char *path;
+ int i;
+
+ /* This test isn't 100% certain, but it'll catch at least some
+ invalid uses... */
+ if (pathelts->elt_size != sizeof(char*))
+ return APR_EINVAL;
+
+ /* Calculate the size of the merged path */
+ for (i = 0; i < pathelts->nelts; ++i)
+ path_size += strlen(((char**)pathelts->elts)[i]);
+
+ if (path_size == 0)
+ {
+ *liststr = NULL;
+ return APR_SUCCESS;
+ }
+
+ if (i > 0) /* Add space for the separators */
+ path_size += (i - 1);
+
+ /* Merge the path components */
+ path = *liststr = apr_palloc(p, path_size + 1);
+ for (i = 0; i < pathelts->nelts; ++i)
+ {
+ /* ### Hmmmm. Calling strlen twice on the same string. Yuck.
+ But is is better than reallocation in apr_pstrcat? */
+ const char *part = ((char**)pathelts->elts)[i];
+ apr_size_t part_size = strlen(part);
+ if (part_size == 0) /* Ignore empty path components. */
+ continue;
+
+ if (i > 0)
+ *path++ = separator;
+ memcpy(path, part, part_size);
+ path += part_size;
+ }
+ *path = '\0';
+ return APR_SUCCESS;
+}
diff --git a/file_io/unix/filestat.c b/file_io/unix/filestat.c
new file mode 100644
index 0000000..220efd0
--- /dev/null
+++ b/file_io/unix/filestat.c
@@ -0,0 +1,339 @@
+/* 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_errno.h"
+
+#ifdef HAVE_UTIME
+#include <utime.h>
+#endif
+
+static apr_filetype_e filetype_from_mode(mode_t mode)
+{
+ apr_filetype_e type;
+
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ type = APR_REG; break;
+ case S_IFDIR:
+ type = APR_DIR; break;
+ case S_IFLNK:
+ type = APR_LNK; break;
+ case S_IFCHR:
+ type = APR_CHR; break;
+ case S_IFBLK:
+ type = APR_BLK; break;
+#if defined(S_IFFIFO)
+ case S_IFFIFO:
+ type = APR_PIPE; break;
+#endif
+#if !defined(BEOS) && defined(S_IFSOCK)
+ case S_IFSOCK:
+ type = APR_SOCK; break;
+#endif
+
+ default:
+ /* Work around missing S_IFxxx values above
+ * for Linux et al.
+ */
+#if !defined(S_IFFIFO) && defined(S_ISFIFO)
+ if (S_ISFIFO(mode)) {
+ type = APR_PIPE;
+ } else
+#endif
+#if !defined(BEOS) && !defined(S_IFSOCK) && defined(S_ISSOCK)
+ if (S_ISSOCK(mode)) {
+ type = APR_SOCK;
+ } else
+#endif
+ type = APR_UNKFILE;
+ }
+ return type;
+}
+
+static void fill_out_finfo(apr_finfo_t *finfo, struct_stat *info,
+ apr_int32_t wanted)
+{
+ finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
+ | APR_FINFO_OWNER | APR_FINFO_PROT;
+ finfo->protection = apr_unix_mode2perms(info->st_mode);
+ finfo->filetype = filetype_from_mode(info->st_mode);
+ finfo->user = info->st_uid;
+ finfo->group = info->st_gid;
+ finfo->size = info->st_size;
+ finfo->device = info->st_dev;
+ finfo->nlink = info->st_nlink;
+
+ /* Check for overflow if storing a 64-bit st_ino in a 32-bit
+ * apr_ino_t for LFS builds: */
+ if (sizeof(apr_ino_t) >= sizeof(info->st_ino)
+ || (apr_ino_t)info->st_ino == info->st_ino) {
+ finfo->inode = info->st_ino;
+ } else {
+ finfo->valid &= ~APR_FINFO_INODE;
+ }
+
+ apr_time_ansi_put(&finfo->atime, info->st_atime);
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+ finfo->atime += info->st_atim.tv_nsec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
+ finfo->atime += info->st_atimensec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_ATIME_N)
+ finfo->atime += info->st_atime_n / APR_TIME_C(1000);
+#endif
+
+ apr_time_ansi_put(&finfo->mtime, info->st_mtime);
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ finfo->mtime += info->st_mtim.tv_nsec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+ finfo->mtime += info->st_mtimensec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_MTIME_N)
+ finfo->mtime += info->st_mtime_n / APR_TIME_C(1000);
+#endif
+
+ apr_time_ansi_put(&finfo->ctime, info->st_ctime);
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+ finfo->ctime += info->st_ctim.tv_nsec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
+ finfo->ctime += info->st_ctimensec / APR_TIME_C(1000);
+#elif defined(HAVE_STRUCT_STAT_ST_CTIME_N)
+ finfo->ctime += info->st_ctime_n / APR_TIME_C(1000);
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+#ifdef DEV_BSIZE
+ finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
+#else
+ finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
+#endif
+ finfo->valid |= APR_FINFO_CSIZE;
+#endif
+}
+
+apr_status_t apr_file_info_get_locked(apr_finfo_t *finfo, apr_int32_t wanted,
+ apr_file_t *thefile)
+{
+ struct_stat info;
+
+ if (thefile->buffered) {
+ apr_status_t rv = apr_file_flush_locked(thefile);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (fstat(thefile->filedes, &info) == 0) {
+ finfo->pool = thefile->pool;
+ finfo->fname = thefile->fname;
+ fill_out_finfo(finfo, &info, wanted);
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_info_get(apr_finfo_t *finfo,
+ apr_int32_t wanted,
+ apr_file_t *thefile)
+{
+ struct_stat info;
+
+ if (thefile->buffered) {
+ apr_status_t rv = apr_file_flush(thefile);
+ if (rv != APR_SUCCESS)
+ return rv;
+ }
+
+ if (fstat(thefile->filedes, &info) == 0) {
+ finfo->pool = thefile->pool;
+ finfo->fname = thefile->fname;
+ fill_out_finfo(finfo, &info, wanted);
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_perms_set(const char *fname,
+ apr_fileperms_t perms)
+{
+ mode_t mode = apr_unix_perms2mode(perms);
+
+ if (chmod(fname, mode) == -1)
+ return errno;
+ 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)
+{
+ apr_status_t status;
+ apr_finfo_t finfo;
+
+ /* Don't do anything if we can't handle the requested attributes */
+ if (!(attr_mask & (APR_FILE_ATTR_READONLY
+ | APR_FILE_ATTR_EXECUTABLE)))
+ return APR_SUCCESS;
+
+ status = apr_stat(&finfo, fname, APR_FINFO_PROT, pool);
+ if (status)
+ return status;
+
+ /* ### TODO: should added bits be umask'd? */
+ if (attr_mask & APR_FILE_ATTR_READONLY)
+ {
+ if (attributes & APR_FILE_ATTR_READONLY)
+ {
+ finfo.protection &= ~APR_UWRITE;
+ finfo.protection &= ~APR_GWRITE;
+ finfo.protection &= ~APR_WWRITE;
+ }
+ else
+ {
+ /* ### umask this! */
+ finfo.protection |= APR_UWRITE;
+ finfo.protection |= APR_GWRITE;
+ finfo.protection |= APR_WWRITE;
+ }
+ }
+
+ if (attr_mask & APR_FILE_ATTR_EXECUTABLE)
+ {
+ if (attributes & APR_FILE_ATTR_EXECUTABLE)
+ {
+ /* ### umask this! */
+ finfo.protection |= APR_UEXECUTE;
+ finfo.protection |= APR_GEXECUTE;
+ finfo.protection |= APR_WEXECUTE;
+ }
+ else
+ {
+ finfo.protection &= ~APR_UEXECUTE;
+ finfo.protection &= ~APR_GEXECUTE;
+ finfo.protection &= ~APR_WEXECUTE;
+ }
+ }
+
+ return apr_file_perms_set(fname, finfo.protection);
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_mtime_set(const char *fname,
+ apr_time_t mtime,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
+ apr_finfo_t finfo;
+
+ status = apr_stat(&finfo, fname, APR_FINFO_ATIME, pool);
+ if (status) {
+ return status;
+ }
+
+#ifdef HAVE_UTIMES
+ {
+ struct timeval tvp[2];
+
+ tvp[0].tv_sec = apr_time_sec(finfo.atime);
+ tvp[0].tv_usec = apr_time_usec(finfo.atime);
+ tvp[1].tv_sec = apr_time_sec(mtime);
+ tvp[1].tv_usec = apr_time_usec(mtime);
+
+ if (utimes(fname, tvp) == -1) {
+ return errno;
+ }
+ }
+#elif defined(HAVE_UTIME)
+ {
+ struct utimbuf buf;
+
+ buf.actime = (time_t) (finfo.atime / APR_USEC_PER_SEC);
+ buf.modtime = (time_t) (mtime / APR_USEC_PER_SEC);
+
+ if (utime(fname, &buf) == -1) {
+ return errno;
+ }
+ }
+#else
+ return APR_ENOTIMPL;
+#endif
+
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo,
+ const char *fname,
+ apr_int32_t wanted, apr_pool_t *pool)
+{
+ struct_stat info;
+ int srv;
+
+ if (wanted & APR_FINFO_LINK)
+ srv = lstat(fname, &info);
+ else
+ srv = stat(fname, &info);
+
+ if (srv == 0) {
+ finfo->pool = pool;
+ finfo->fname = fname;
+ fill_out_finfo(finfo, &info, wanted);
+ if (wanted & APR_FINFO_LINK)
+ wanted &= ~APR_FINFO_LINK;
+ return (wanted & ~finfo->valid) ? APR_INCOMPLETE : APR_SUCCESS;
+ }
+ else {
+#if !defined(ENOENT) || !defined(ENOTDIR)
+#error ENOENT || ENOTDIR not defined; please see the
+#error comments at this line in the source for a workaround.
+ /*
+ * If ENOENT || ENOTDIR is not defined in one of the your OS's
+ * include files, APR cannot report a good reason why the stat()
+ * of the file failed; there are cases where it can fail even though
+ * the file exists. This opens holes in Apache, for example, because
+ * it becomes possible for someone to get a directory listing of a
+ * directory even though there is an index (eg. index.html) file in
+ * it. If you do not have a problem with this, delete the above
+ * #error lines and start the compile again. If you need to do this,
+ * please submit a bug report to http://www.apache.org/bug_report.html
+ * letting us know that you needed to do this. Please be sure to
+ * include the operating system you are using.
+ */
+ /* WARNING: All errors will be handled as not found
+ */
+#if !defined(ENOENT)
+ return APR_ENOENT;
+#else
+ /* WARNING: All errors but not found will be handled as not directory
+ */
+ if (errno != ENOENT)
+ return APR_ENOENT;
+ else
+ return errno;
+#endif
+#else /* All was defined well, report the usual: */
+ return errno;
+#endif
+ }
+}
+
+
diff --git a/file_io/unix/flock.c b/file_io/unix/flock.c
new file mode 100644
index 0000000..01e8a63
--- /dev/null
+++ b/file_io/unix/flock.c
@@ -0,0 +1,120 @@
+/* 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"
+
+#if APR_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type)
+{
+ int rc;
+
+#if defined(HAVE_FCNTL_H)
+ {
+ struct flock l = { 0 };
+ int fc;
+
+ l.l_whence = SEEK_SET; /* count l_start from start of file */
+ l.l_start = 0; /* lock from start of file */
+ l.l_len = 0; /* lock to end of file */
+ if ((type & APR_FLOCK_TYPEMASK) == APR_FLOCK_SHARED)
+ l.l_type = F_RDLCK;
+ else
+ l.l_type = F_WRLCK;
+
+ fc = (type & APR_FLOCK_NONBLOCK) ? F_SETLK : F_SETLKW;
+
+ /* keep trying if fcntl() gets interrupted (by a signal) */
+ while ((rc = fcntl(thefile->filedes, fc, &l)) < 0 && errno == EINTR)
+ continue;
+
+ if (rc == -1) {
+ /* on some Unix boxes (e.g., Tru64), we get EACCES instead
+ * of EAGAIN; we don't want APR_STATUS_IS_EAGAIN() matching EACCES
+ * since that breaks other things, so fix up the retcode here
+ */
+ if (errno == EACCES) {
+ return EAGAIN;
+ }
+ return errno;
+ }
+ }
+#elif defined(HAVE_SYS_FILE_H)
+ {
+ int ltype;
+
+ if ((type & APR_FLOCK_TYPEMASK) == APR_FLOCK_SHARED)
+ ltype = LOCK_SH;
+ else
+ ltype = LOCK_EX;
+ if ((type & APR_FLOCK_NONBLOCK) != 0)
+ ltype |= LOCK_NB;
+
+ /* keep trying if flock() gets interrupted (by a signal) */
+ while ((rc = flock(thefile->filedes, ltype)) < 0 && errno == EINTR)
+ continue;
+
+ if (rc == -1)
+ return errno;
+ }
+#else
+#error No file locking mechanism is available.
+#endif
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_unlock(apr_file_t *thefile)
+{
+ int rc;
+
+#if defined(HAVE_FCNTL_H)
+ {
+ struct flock l = { 0 };
+
+ l.l_whence = SEEK_SET; /* count l_start from start of file */
+ l.l_start = 0; /* lock from start of file */
+ l.l_len = 0; /* lock to end of file */
+ l.l_type = F_UNLCK;
+
+ /* keep trying if fcntl() gets interrupted (by a signal) */
+ while ((rc = fcntl(thefile->filedes, F_SETLKW, &l)) < 0
+ && errno == EINTR)
+ continue;
+
+ if (rc == -1)
+ return errno;
+ }
+#elif defined(HAVE_SYS_FILE_H)
+ {
+ /* keep trying if flock() gets interrupted (by a signal) */
+ while ((rc = flock(thefile->filedes, LOCK_UN)) < 0 && errno == EINTR)
+ continue;
+
+ if (rc == -1)
+ return errno;
+ }
+#else
+#error No file locking mechanism is available.
+#endif
+
+ return APR_SUCCESS;
+}
diff --git a/file_io/unix/fullrw.c b/file_io/unix/fullrw.c
new file mode 100644
index 0000000..3c67f65
--- /dev/null
+++ b/file_io/unix/fullrw.c
@@ -0,0 +1,111 @@
+/* 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_file_io.h"
+
+
+APR_DECLARE(apr_status_t) apr_file_read_full(apr_file_t *thefile, void *buf,
+ apr_size_t nbytes,
+ apr_size_t *bytes_read)
+{
+ apr_status_t status;
+ apr_size_t total_read = 0;
+
+ do {
+ apr_size_t amt = nbytes;
+
+ status = apr_file_read(thefile, buf, &amt);
+ buf = (char *)buf + amt;
+ nbytes -= amt;
+ total_read += amt;
+ } while (status == APR_SUCCESS && nbytes > 0);
+
+ if (bytes_read != NULL)
+ *bytes_read = total_read;
+
+ return status;
+}
+
+APR_DECLARE(apr_status_t) apr_file_write_full(apr_file_t *thefile,
+ const void *buf,
+ apr_size_t nbytes,
+ apr_size_t *bytes_written)
+{
+ apr_status_t status;
+ apr_size_t total_written = 0;
+
+ do {
+ apr_size_t amt = nbytes;
+
+ status = apr_file_write(thefile, buf, &amt);
+ buf = (char *)buf + amt;
+ nbytes -= amt;
+ total_written += amt;
+ } while (status == APR_SUCCESS && nbytes > 0);
+
+ if (bytes_written != NULL)
+ *bytes_written = total_written;
+
+ return status;
+}
+
+APR_DECLARE(apr_status_t) apr_file_writev_full(apr_file_t *thefile,
+ const struct iovec *vec,
+ apr_size_t nvec,
+ apr_size_t *bytes_written)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_size_t i;
+ apr_size_t amt = 0;
+ apr_size_t total = 0;
+
+ for (i = 0; i < nvec; i++) {
+ total += vec[i].iov_len;
+ }
+
+ rv = apr_file_writev(thefile, vec, nvec, &amt);
+
+ if (bytes_written != NULL)
+ *bytes_written = amt;
+
+ if (rv != APR_SUCCESS || (amt == total)) {
+ return rv;
+ }
+
+ for (i = 0; i < nvec && amt; i++) {
+ if (amt >= vec[i].iov_len) {
+ amt -= vec[i].iov_len;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (amt) {
+ rv = apr_file_write_full(thefile, (const char *)vec[i].iov_base + amt,
+ vec[i].iov_len - amt, NULL);
+ }
+
+ for (; i < nvec && rv == APR_SUCCESS; i++) {
+ rv = apr_file_write_full(thefile, vec[i].iov_base,
+ vec[i].iov_len, &amt);
+ }
+
+ if (bytes_written != NULL)
+ *bytes_written = total;
+
+ return rv;
+}
diff --git a/file_io/unix/mktemp.c b/file_io/unix/mktemp.c
new file mode 100644
index 0000000..30bc78f
--- /dev/null
+++ b/file_io/unix/mktemp.c
@@ -0,0 +1,226 @@
+/* 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.
+ */
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "apr_private.h"
+#include "apr_file_io.h" /* prototype of apr_mkstemp() */
+#include "apr_strings.h" /* prototype of apr_mkstemp() */
+#include "apr_arch_file_io.h" /* prototype of apr_mkstemp() */
+#include "apr_portable.h" /* for apr_os_file_put() */
+#include "apr_arch_inherit.h"
+
+#ifndef HAVE_MKSTEMP
+
+#if defined(SVR4) || defined(WIN32) || defined(NETWARE)
+#ifdef SVR4
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#endif
+#define arc4random() rand()
+#define seedrandom(a) srand(a)
+#else
+#if APR_HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#define arc4random() random()
+#define seedrandom(a) srandom(a)
+#endif
+
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if APR_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+static const unsigned char padchar[] =
+"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+static apr_uint32_t randseed=0;
+
+static int gettemp(char *path, apr_file_t **doopen, apr_int32_t flags, apr_pool_t *p)
+{
+ register char *start, *trv, *suffp;
+ char *pad;
+ apr_finfo_t sbuf;
+ apr_status_t rv;
+ apr_uint32_t randnum;
+
+ if (randseed==0) {
+ randseed = (int)apr_time_now();
+ seedrandom(randseed);
+ }
+
+ for (trv = path; *trv; ++trv)
+ ;
+ suffp = trv;
+ --trv;
+ if (trv < path) {
+ return APR_EINVAL;
+ }
+
+ /* Fill space with random characters */
+ while (*trv == 'X') {
+ randnum = arc4random() % (sizeof(padchar) - 1);
+ *trv-- = padchar[randnum];
+ }
+ start = trv + 1;
+
+ /*
+ * check the target directory.
+ */
+ for (;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ rv = apr_stat(&sbuf, path, APR_FINFO_TYPE, p);
+ *trv = '/';
+ if (rv != APR_SUCCESS)
+ return rv;
+ if (sbuf.filetype != APR_DIR) {
+ return APR_ENOTDIR;
+ }
+ break;
+ }
+ }
+
+ for (;;) {
+ if ((rv = apr_file_open(doopen, path, flags,
+ APR_UREAD | APR_UWRITE, p)) == APR_SUCCESS)
+ return APR_SUCCESS;
+ if (!APR_STATUS_IS_EEXIST(rv))
+ return rv;
+
+ /* If we have a collision, cycle through the space of filenames */
+ for (trv = start;;) {
+ if (*trv == '\0' || trv == suffp)
+ return APR_EINVAL; /* XXX: is this the correct return code? */
+ pad = strchr((char *)padchar, *trv);
+ if (pad == NULL || !*++pad) {
+ *trv++ = padchar[0];
+ }
+ else {
+ *trv++ = *pad;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+#else
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h> /* for mkstemp() - Single Unix */
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h> /* for mkstemp() - FreeBSD */
+#endif
+#endif /* !defined(HAVE_MKSTEMP) */
+
+APR_DECLARE(apr_status_t) apr_file_mktemp(apr_file_t **fp, char *template, apr_int32_t flags, apr_pool_t *p)
+{
+#ifdef HAVE_MKSTEMP
+ int fd;
+#endif
+ flags = (!flags) ? APR_FOPEN_CREATE | APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_EXCL |
+ APR_FOPEN_DELONCLOSE : flags;
+#ifndef HAVE_MKSTEMP
+ return gettemp(template, fp, flags, p);
+#else
+
+#ifdef HAVE_MKSTEMP64
+ fd = mkstemp64(template);
+#else
+ fd = mkstemp(template);
+#endif
+
+ if (fd == -1) {
+ return errno;
+ }
+ /* XXX: We must reset several flags values as passed-in, since
+ * mkstemp didn't subscribe to our preference flags.
+ *
+ * We either have to unset the flags, or fix up the fd and other
+ * xthread and inherit bits appropriately. Since gettemp() above
+ * calls apr_file_open, our flags are respected in that code path.
+ */
+ apr_os_file_put(fp, &fd, flags, p);
+ (*fp)->fname = apr_pstrdup(p, template);
+
+ if (!(flags & APR_FOPEN_NOCLEANUP)) {
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD)) == -1)
+ return errno;
+
+ flags |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1)
+ return errno;
+
+ apr_pool_cleanup_register((*fp)->pool, (void *)(*fp),
+ apr_unix_file_cleanup,
+ apr_unix_child_file_cleanup);
+
+ /* Clear APR_FOPEN_NOCLEANUP set by apr_os_file_put() */
+ (*fp)->flags &= ~APR_FOPEN_NOCLEANUP;
+ }
+#endif
+ return APR_SUCCESS;
+}
+
diff --git a/file_io/unix/open.c b/file_io/unix/open.c
new file mode 100644
index 0000000..49eb727
--- /dev/null
+++ b/file_io/unix/open.c
@@ -0,0 +1,417 @@
+/* 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_strings.h"
+#include "apr_portable.h"
+#include "apr_thread_mutex.h"
+#include "apr_arch_inherit.h"
+
+#ifdef NETWARE
+#include "nks/dirio.h"
+#include "apr_hash.h"
+#include "fsio.h"
+#endif
+
+static apr_status_t file_cleanup(apr_file_t *file, int is_child)
+{
+ apr_status_t rv = APR_SUCCESS;
+ int fd = file->filedes;
+
+ /* Set file descriptor to -1 before close(), so that there is no
+ * chance of returning an already closed FD from apr_os_file_get().
+ */
+ file->filedes = -1;
+
+ if (close(fd) == 0) {
+ /* Only the parent process should delete the file! */
+ if (!is_child && (file->flags & APR_FOPEN_DELONCLOSE)) {
+ unlink(file->fname);
+ }
+#if APR_HAS_THREADS
+ if (file->thlock) {
+ rv = apr_thread_mutex_destroy(file->thlock);
+ }
+#endif
+ }
+ else {
+ /* Restore, close() was not successful. */
+ file->filedes = fd;
+
+ /* Are there any error conditions other than EINTR or EBADF? */
+ rv = errno;
+ }
+#ifndef WAITIO_USES_POLL
+ if (file->pollset != NULL) {
+ apr_status_t pollset_rv = apr_pollset_destroy(file->pollset);
+ /* If the file close failed, return its error value,
+ * not apr_pollset_destroy()'s.
+ */
+ if (rv == APR_SUCCESS) {
+ rv = pollset_rv;
+ }
+ }
+#endif /* !WAITIO_USES_POLL */
+ return rv;
+}
+
+apr_status_t apr_unix_file_cleanup(void *thefile)
+{
+ apr_file_t *file = thefile;
+ apr_status_t flush_rv = APR_SUCCESS, rv = APR_SUCCESS;
+
+ if (file->buffered) {
+ flush_rv = apr_file_flush(file);
+ }
+
+ rv = file_cleanup(file, 0);
+
+ return rv != APR_SUCCESS ? rv : flush_rv;
+}
+
+apr_status_t apr_unix_child_file_cleanup(void *thefile)
+{
+ return file_cleanup(thefile, 1);
+}
+
+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)
+{
+ apr_os_file_t fd;
+ int oflags = 0;
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *thlock;
+ apr_status_t rv;
+#endif
+
+ if ((flag & APR_FOPEN_READ) && (flag & APR_FOPEN_WRITE)) {
+ oflags = O_RDWR;
+ }
+ else if (flag & APR_FOPEN_READ) {
+ oflags = O_RDONLY;
+ }
+ else if (flag & APR_FOPEN_WRITE) {
+ oflags = O_WRONLY;
+ }
+ else {
+ return APR_EACCES;
+ }
+
+ if (flag & APR_FOPEN_CREATE) {
+ oflags |= O_CREAT;
+ if (flag & APR_FOPEN_EXCL) {
+ oflags |= O_EXCL;
+ }
+ }
+ if ((flag & APR_FOPEN_EXCL) && !(flag & APR_FOPEN_CREATE)) {
+ return APR_EACCES;
+ }
+
+ if (flag & APR_FOPEN_APPEND) {
+ oflags |= O_APPEND;
+ }
+ if (flag & APR_FOPEN_TRUNCATE) {
+ oflags |= O_TRUNC;
+ }
+#ifdef O_BINARY
+ if (flag & APR_FOPEN_BINARY) {
+ oflags |= O_BINARY;
+ }
+#endif
+
+ if (flag & APR_FOPEN_NONBLOCK) {
+#ifdef O_NONBLOCK
+ oflags |= O_NONBLOCK;
+#else
+ return APR_ENOTIMPL;
+#endif
+ }
+
+#ifdef O_CLOEXEC
+ /* Introduced in Linux 2.6.23. Silently ignored on earlier Linux kernels.
+ */
+ if (!(flag & APR_FOPEN_NOCLEANUP)) {
+ oflags |= O_CLOEXEC;
+}
+#endif
+
+#if APR_HAS_LARGE_FILES && defined(_LARGEFILE64_SOURCE)
+ oflags |= O_LARGEFILE;
+#elif defined(O_LARGEFILE)
+ if (flag & APR_FOPEN_LARGEFILE) {
+ oflags |= O_LARGEFILE;
+ }
+#endif
+
+#if APR_HAS_THREADS
+ if ((flag & APR_FOPEN_BUFFERED) && (flag & APR_FOPEN_XTHREAD)) {
+ rv = apr_thread_mutex_create(&thlock,
+ APR_THREAD_MUTEX_DEFAULT, pool);
+ if (rv) {
+ return rv;
+ }
+ }
+#endif
+
+ if (perm == APR_OS_DEFAULT) {
+ fd = open(fname, oflags, 0666);
+ }
+ else {
+ fd = open(fname, oflags, apr_unix_perms2mode(perm));
+ }
+ if (fd < 0) {
+ return errno;
+ }
+ if (!(flag & APR_FOPEN_NOCLEANUP)) {
+#ifdef O_CLOEXEC
+ static int has_o_cloexec = 0;
+ if (!has_o_cloexec)
+#endif
+ {
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFD)) == -1) {
+ close(fd);
+ return errno;
+ }
+ if ((flags & FD_CLOEXEC) == 0) {
+ flags |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1) {
+ close(fd);
+ return errno;
+ }
+ }
+#ifdef O_CLOEXEC
+ else {
+ has_o_cloexec = 1;
+ }
+#endif
+ }
+ }
+
+ (*new) = (apr_file_t *)apr_pcalloc(pool, sizeof(apr_file_t));
+ (*new)->pool = pool;
+ (*new)->flags = flag;
+ (*new)->filedes = fd;
+
+ (*new)->fname = apr_pstrdup(pool, fname);
+
+ (*new)->blocking = BLK_ON;
+ (*new)->buffered = (flag & APR_FOPEN_BUFFERED) > 0;
+
+ if ((*new)->buffered) {
+ (*new)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
+ (*new)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
+#if APR_HAS_THREADS
+ if ((*new)->flags & APR_FOPEN_XTHREAD) {
+ (*new)->thlock = thlock;
+ }
+#endif
+ }
+ else {
+ (*new)->buffer = NULL;
+ }
+
+ (*new)->is_pipe = 0;
+ (*new)->timeout = -1;
+ (*new)->ungetchar = -1;
+ (*new)->eof_hit = 0;
+ (*new)->filePtr = 0;
+ (*new)->bufpos = 0;
+ (*new)->dataRead = 0;
+ (*new)->direction = 0;
+#ifndef WAITIO_USES_POLL
+ /* Start out with no pollset. apr_wait_for_io_or_timeout() will
+ * initialize the pollset if needed.
+ */
+ (*new)->pollset = NULL;
+#endif
+ if (!(flag & APR_FOPEN_NOCLEANUP)) {
+ apr_pool_cleanup_register((*new)->pool, (void *)(*new),
+ apr_unix_file_cleanup,
+ apr_unix_child_file_cleanup);
+ }
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_close(apr_file_t *file)
+{
+ return apr_pool_cleanup_run(file->pool, file, apr_unix_file_cleanup);
+}
+
+APR_DECLARE(apr_status_t) apr_file_remove(const char *path, apr_pool_t *pool)
+{
+ if (unlink(path) == 0) {
+ return APR_SUCCESS;
+ }
+ else {
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_rename(const char *from_path,
+ const char *to_path,
+ apr_pool_t *p)
+{
+ if (rename(from_path, to_path) != 0) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_os_file_get(apr_os_file_t *thefile,
+ apr_file_t *file)
+{
+ *thefile = file->filedes;
+ 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)
+{
+ int *dafile = thefile;
+
+ (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
+ (*file)->pool = pool;
+ (*file)->eof_hit = 0;
+ (*file)->blocking = BLK_UNKNOWN; /* in case it is a pipe */
+ (*file)->timeout = -1;
+ (*file)->ungetchar = -1; /* no char avail */
+ (*file)->filedes = *dafile;
+ (*file)->flags = flags | APR_FOPEN_NOCLEANUP;
+ (*file)->buffered = (flags & APR_FOPEN_BUFFERED) > 0;
+
+#ifndef WAITIO_USES_POLL
+ /* Start out with no pollset. apr_wait_for_io_or_timeout() will
+ * initialize the pollset if needed.
+ */
+ (*file)->pollset = NULL;
+#endif
+
+ if ((*file)->buffered) {
+ (*file)->buffer = apr_palloc(pool, APR_FILE_DEFAULT_BUFSIZE);
+ (*file)->bufsize = APR_FILE_DEFAULT_BUFSIZE;
+#if APR_HAS_THREADS
+ if ((*file)->flags & APR_FOPEN_XTHREAD) {
+ apr_status_t rv;
+ rv = apr_thread_mutex_create(&((*file)->thlock),
+ APR_THREAD_MUTEX_DEFAULT, pool);
+ if (rv) {
+ return rv;
+ }
+ }
+#endif
+ }
+ 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)
+{
+ int fd = STDERR_FILENO;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
+}
+
+APR_DECLARE(apr_status_t) apr_file_open_flags_stdout(apr_file_t **thefile,
+ apr_int32_t flags,
+ apr_pool_t *pool)
+{
+ int fd = STDOUT_FILENO;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_WRITE, pool);
+}
+
+APR_DECLARE(apr_status_t) apr_file_open_flags_stdin(apr_file_t **thefile,
+ apr_int32_t flags,
+ apr_pool_t *pool)
+{
+ int fd = STDIN_FILENO;
+
+ return apr_os_file_put(thefile, &fd, flags | APR_FOPEN_READ, pool);
+}
+
+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_IMPLEMENT_INHERIT_SET(file, flags, pool, apr_unix_file_cleanup)
+
+/* We need to do this by hand instead of using APR_IMPLEMENT_INHERIT_UNSET
+ * because the macro sets both cleanups to the same function, which is not
+ * suitable on Unix (see PR 41119). */
+APR_DECLARE(apr_status_t) apr_file_inherit_unset(apr_file_t *thefile)
+{
+ if (thefile->flags & APR_FOPEN_NOCLEANUP) {
+ return APR_EINVAL;
+ }
+ if (thefile->flags & APR_INHERIT) {
+ int flags;
+
+ if ((flags = fcntl(thefile->filedes, F_GETFD)) == -1)
+ return errno;
+
+ flags |= FD_CLOEXEC;
+ if (fcntl(thefile->filedes, F_SETFD, flags) == -1)
+ return errno;
+
+ thefile->flags &= ~APR_INHERIT;
+ apr_pool_child_cleanup_set(thefile->pool,
+ (void *)thefile,
+ apr_unix_file_cleanup,
+ apr_unix_child_file_cleanup);
+ }
+ return APR_SUCCESS;
+}
+
+APR_POOL_IMPLEMENT_ACCESSOR(file)
+
+APR_DECLARE(apr_status_t) apr_file_link(const char *from_path,
+ const char *to_path)
+{
+ if (link(from_path, to_path) == -1) {
+ return errno;
+ }
+
+ return APR_SUCCESS;
+}
diff --git a/file_io/unix/pipe.c b/file_io/unix/pipe.c
new file mode 100644
index 0000000..7be16e5
--- /dev/null
+++ b/file_io/unix/pipe.c
@@ -0,0 +1,292 @@
+/* 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_strings.h"
+#include "apr_portable.h"
+
+#include "apr_arch_inherit.h"
+
+/* Figure out how to get pipe block/nonblock on BeOS...
+ * Basically, BONE7 changed things again so that ioctl didn't work,
+ * but now fcntl does, hence we need to do this extra checking.
+ * The joys of beta programs. :-)
+ */
+#if defined(BEOS)
+#if !defined(BONE7)
+# define BEOS_BLOCKING 1
+#else
+# define BEOS_BLOCKING 0
+#endif
+#endif
+
+static apr_status_t pipeblock(apr_file_t *thepipe)
+{
+#if !defined(BEOS) || !BEOS_BLOCKING
+ int fd_flags;
+
+ fd_flags = fcntl(thepipe->filedes, F_GETFL, 0);
+# if defined(O_NONBLOCK)
+ fd_flags &= ~O_NONBLOCK;
+# elif defined(O_NDELAY)
+ fd_flags &= ~O_NDELAY;
+# elif defined(O_FNDELAY)
+ fd_flags &= ~O_FNDELAY;
+# else
+ /* XXXX: this breaks things, but an alternative isn't obvious...*/
+ return APR_ENOTIMPL;
+# endif
+ if (fcntl(thepipe->filedes, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+#else /* BEOS_BLOCKING */
+
+# if BEOS_BONE /* This only works on BONE 0-6 */
+ int on = 0;
+ if (ioctl(thepipe->filedes, FIONBIO, &on, sizeof(on)) < 0) {
+ return errno;
+ }
+# else /* "classic" BeOS doesn't support this at all */
+ return APR_ENOTIMPL;
+# endif
+
+#endif /* !BEOS_BLOCKING */
+
+ thepipe->blocking = BLK_ON;
+ return APR_SUCCESS;
+}
+
+static apr_status_t pipenonblock(apr_file_t *thepipe)
+{
+#if !defined(BEOS) || !BEOS_BLOCKING
+ int fd_flags = fcntl(thepipe->filedes, F_GETFL, 0);
+
+# if defined(O_NONBLOCK)
+ fd_flags |= O_NONBLOCK;
+# elif defined(O_NDELAY)
+ fd_flags |= O_NDELAY;
+# elif defined(O_FNDELAY)
+ fd_flags |= O_FNDELAY;
+# else
+ /* XXXX: this breaks things, but an alternative isn't obvious...*/
+ return APR_ENOTIMPL;
+# endif
+ if (fcntl(thepipe->filedes, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+
+#else /* BEOS_BLOCKING */
+
+# if BEOS_BONE /* This only works on BONE 0-6 */
+ int on = 1;
+ if (ioctl(thepipe->filedes, FIONBIO, &on, sizeof(on)) < 0) {
+ return errno;
+ }
+# else /* "classic" BeOS doesn't support this at all */
+ return APR_ENOTIMPL;
+# endif
+
+#endif /* !BEOS_BLOCKING */
+
+ thepipe->blocking = BLK_OFF;
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_set(apr_file_t *thepipe, apr_interval_time_t timeout)
+{
+ if (thepipe->is_pipe == 1) {
+ thepipe->timeout = timeout;
+ if (timeout >= 0) {
+ if (thepipe->blocking != BLK_OFF) { /* blocking or unknown state */
+ return pipenonblock(thepipe);
+ }
+ }
+ else {
+ if (thepipe->blocking != BLK_ON) { /* non-blocking or unknown state */
+ return pipeblock(thepipe);
+ }
+ }
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_timeout_get(apr_file_t *thepipe, apr_interval_time_t *timeout)
+{
+ if (thepipe->is_pipe == 1) {
+ *timeout = thepipe->timeout;
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+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)
+{
+ int *dafile = thefile;
+
+ (*file) = apr_pcalloc(pool, sizeof(apr_file_t));
+ (*file)->pool = pool;
+ (*file)->eof_hit = 0;
+ (*file)->is_pipe = 1;
+ (*file)->blocking = BLK_UNKNOWN; /* app needs to make a timeout call */
+ (*file)->timeout = -1;
+ (*file)->ungetchar = -1; /* no char avail */
+ (*file)->filedes = *dafile;
+ if (!register_cleanup) {
+ (*file)->flags = APR_FOPEN_NOCLEANUP;
+ }
+ (*file)->buffered = 0;
+#if APR_HAS_THREADS
+ (*file)->thlock = NULL;
+#endif
+ if (register_cleanup) {
+ apr_pool_cleanup_register((*file)->pool, (void *)(*file),
+ apr_unix_file_cleanup,
+ apr_pool_cleanup_null);
+ }
+#ifndef WAITIO_USES_POLL
+ /* Start out with no pollset. apr_wait_for_io_or_timeout() will
+ * initialize the pollset if needed.
+ */
+ (*file)->pollset = NULL;
+#endif
+ 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 file_pipe_create(apr_file_t **in, apr_file_t **out,
+ apr_pool_t *pool_in, apr_pool_t *pool_out)
+{
+ int filedes[2];
+
+ if (pipe(filedes) == -1) {
+ return errno;
+ }
+
+ (*in) = (apr_file_t *)apr_pcalloc(pool_in, sizeof(apr_file_t));
+ (*in)->pool = pool_in;
+ (*in)->filedes = filedes[0];
+ (*in)->is_pipe = 1;
+ (*in)->fname = NULL;
+ (*in)->buffered = 0;
+ (*in)->blocking = BLK_ON;
+ (*in)->timeout = -1;
+ (*in)->ungetchar = -1;
+ (*in)->flags = APR_INHERIT;
+#if APR_HAS_THREADS
+ (*in)->thlock = NULL;
+#endif
+#ifndef WAITIO_USES_POLL
+ (*in)->pollset = NULL;
+#endif
+ (*out) = (apr_file_t *)apr_pcalloc(pool_out, sizeof(apr_file_t));
+ (*out)->pool = pool_out;
+ (*out)->filedes = filedes[1];
+ (*out)->is_pipe = 1;
+ (*out)->fname = NULL;
+ (*out)->buffered = 0;
+ (*out)->blocking = BLK_ON;
+ (*out)->flags = APR_INHERIT;
+ (*out)->timeout = -1;
+#if APR_HAS_THREADS
+ (*out)->thlock = NULL;
+#endif
+#ifndef WAITIO_USES_POLL
+ (*out)->pollset = NULL;
+#endif
+ apr_pool_cleanup_register((*in)->pool, (void *)(*in), apr_unix_file_cleanup,
+ apr_pool_cleanup_null);
+ apr_pool_cleanup_register((*out)->pool, (void *)(*out), apr_unix_file_cleanup,
+ apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+static void file_pipe_block(apr_file_t **in, apr_file_t **out, apr_int32_t blocking)
+{
+ switch (blocking) {
+ case APR_FULL_BLOCK:
+ break;
+ case APR_READ_BLOCK:
+ apr_file_pipe_timeout_set(*out, 0);
+ break;
+ case APR_WRITE_BLOCK:
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ default:
+ apr_file_pipe_timeout_set(*out, 0);
+ apr_file_pipe_timeout_set(*in, 0);
+ break;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_pipe_create(apr_file_t **in,
+ apr_file_t **out, apr_pool_t *pool)
+{
+ return file_pipe_create(in, out, pool, pool);
+}
+
+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 *pool)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool, pool)) != APR_SUCCESS) {
+ return status;
+ }
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+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)
+{
+ apr_status_t status;
+
+ if ((status = file_pipe_create(in, out, pool_in, pool_out)) != APR_SUCCESS) {
+ return status;
+ }
+
+ file_pipe_block(in, out, blocking);
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_namedpipe_create(const char *filename,
+ apr_fileperms_t perm, apr_pool_t *pool)
+{
+ mode_t mode = apr_unix_perms2mode(perm);
+
+ if (mkfifo(filename, mode) == -1) {
+ return errno;
+ }
+ return APR_SUCCESS;
+}
+
+
+
diff --git a/file_io/unix/readwrite.c b/file_io/unix/readwrite.c
new file mode 100644
index 0000000..866acb8
--- /dev/null
+++ b/file_io/unix/readwrite.c
@@ -0,0 +1,538 @@
+/* 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_strings.h"
+#include "apr_thread_mutex.h"
+#include "apr_support.h"
+
+/* The only case where we don't use wait_for_io_or_timeout is on
+ * pre-BONE BeOS, so this check should be sufficient and simpler */
+#if !defined(BEOS_R5)
+#define USE_WAIT_FOR_IO
+#endif
+
+static apr_status_t file_read_buffered(apr_file_t *thefile, void *buf,
+ apr_size_t *nbytes)
+{
+ apr_ssize_t rv;
+ char *pos = (char *)buf;
+ apr_uint64_t blocksize;
+ apr_uint64_t size = *nbytes;
+
+ if (thefile->direction == 1) {
+ rv = apr_file_flush_locked(thefile);
+ if (rv) {
+ return rv;
+ }
+ thefile->bufpos = 0;
+ thefile->direction = 0;
+ thefile->dataRead = 0;
+ }
+
+ rv = 0;
+ if (thefile->ungetchar != -1) {
+ *pos = (char)thefile->ungetchar;
+ ++pos;
+ --size;
+ thefile->ungetchar = -1;
+ }
+ while (rv == 0 && size > 0) {
+ if (thefile->bufpos >= thefile->dataRead) {
+ int bytesread = read(thefile->filedes, thefile->buffer,
+ thefile->bufsize);
+ if (bytesread == 0) {
+ thefile->eof_hit = TRUE;
+ rv = APR_EOF;
+ break;
+ }
+ else if (bytesread == -1) {
+ rv = errno;
+ break;
+ }
+ thefile->dataRead = bytesread;
+ 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;
+ }
+
+ *nbytes = pos - (char *)buf;
+ if (*nbytes) {
+ rv = 0;
+ }
+ return rv;
+}
+
+APR_DECLARE(apr_status_t) apr_file_read(apr_file_t *thefile, void *buf, apr_size_t *nbytes)
+{
+ apr_ssize_t rv;
+ apr_size_t bytes_read;
+
+ if (*nbytes <= 0) {
+ *nbytes = 0;
+ return APR_SUCCESS;
+ }
+
+ if (thefile->buffered) {
+ file_lock(thefile);
+ rv = file_read_buffered(thefile, buf, nbytes);
+ file_unlock(thefile);
+ return rv;
+ }
+ else {
+ bytes_read = 0;
+ if (thefile->ungetchar != -1) {
+ bytes_read = 1;
+ *(char *)buf = (char)thefile->ungetchar;
+ buf = (char *)buf + 1;
+ (*nbytes)--;
+ thefile->ungetchar = -1;
+ if (*nbytes == 0) {
+ *nbytes = bytes_read;
+ return APR_SUCCESS;
+ }
+ }
+
+ do {
+ rv = read(thefile->filedes, buf, *nbytes);
+ } while (rv == -1 && errno == EINTR);
+#ifdef USE_WAIT_FOR_IO
+ if (rv == -1 &&
+ (errno == EAGAIN || errno == EWOULDBLOCK) &&
+ thefile->timeout != 0) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 1);
+ if (arv != APR_SUCCESS) {
+ *nbytes = bytes_read;
+ return arv;
+ }
+ else {
+ do {
+ rv = read(thefile->filedes, buf, *nbytes);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+#endif
+ *nbytes = bytes_read;
+ if (rv == 0) {
+ thefile->eof_hit = TRUE;
+ return APR_EOF;
+ }
+ if (rv > 0) {
+ *nbytes += rv;
+ return APR_SUCCESS;
+ }
+ return errno;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
+{
+ apr_size_t rv = APR_SUCCESS;
+
+ if (thefile->buffered) {
+ char *pos = (char *)buf;
+ int blocksize;
+ int size = *nbytes;
+
+ file_lock(thefile);
+
+ if ( thefile->direction == 0 ) {
+ /* Position file pointer for writing at the offset we are
+ * logically reading from
+ */
+ apr_int64_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
+ if (offset != thefile->filePtr) {
+ thefile->filePtr = lseek(thefile->filedes, offset, SEEK_SET);
+ if (thefile->filePtr == -1) rv = errno;
+ }
+ thefile->bufpos = thefile->dataRead = 0;
+ thefile->direction = 1;
+ }
+
+ while (rv == 0 && size > 0) {
+ if (thefile->bufpos == thefile->bufsize) /* write buffer is full*/
+ rv = apr_file_flush_locked(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;
+ }
+
+ file_unlock(thefile);
+
+ return rv;
+ }
+ else {
+ do {
+ rv = write(thefile->filedes, buf, *nbytes);
+ } while (rv == (apr_size_t)-1 && errno == EINTR);
+#ifdef USE_WAIT_FOR_IO
+ if (rv == (apr_size_t)-1 &&
+ (errno == EAGAIN || errno == EWOULDBLOCK) &&
+ thefile->timeout != 0) {
+ apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
+ if (arv != APR_SUCCESS) {
+ *nbytes = 0;
+ return arv;
+ }
+ else {
+ do {
+ do {
+ rv = write(thefile->filedes, buf, *nbytes);
+ } while (rv == (apr_size_t)-1 && errno == EINTR);
+ if (rv == (apr_size_t)-1 &&
+ (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ *nbytes /= 2; /* yes, we'll loop if kernel lied
+ * and we can't even write 1 byte
+ */
+ }
+ else {
+ break;
+ }
+ } while (1);
+ }
+ }
+#endif
+ if (rv == (apr_size_t)-1) {
+ (*nbytes) = 0;
+ return errno;
+ }
+ *nbytes = rv;
+ return APR_SUCCESS;
+ }
+}
+
+APR_DECLARE(apr_status_t) apr_file_writev(apr_file_t *thefile, const struct iovec *vec,
+ apr_size_t nvec, apr_size_t *nbytes)
+{
+#ifdef HAVE_WRITEV
+ apr_status_t rv;
+ apr_ssize_t bytes;
+
+ if (thefile->buffered) {
+ file_lock(thefile);
+
+ rv = apr_file_flush_locked(thefile);
+ if (rv != APR_SUCCESS) {
+ file_unlock(thefile);
+ return rv;
+ }
+ if (thefile->direction == 0) {
+ /* Position file pointer for writing at the offset we are
+ * logically reading from
+ */
+ apr_int64_t offset = thefile->filePtr - thefile->dataRead +
+ thefile->bufpos;
+ if (offset != thefile->filePtr) {
+ thefile->filePtr = lseek(thefile->filedes, offset, SEEK_SET);
+ if (thefile->filePtr == -1) rv = errno;
+ }
+ thefile->bufpos = thefile->dataRead = 0;
+ }
+
+ file_unlock(thefile);
+ if (rv) return rv;
+ }
+
+ if ((bytes = writev(thefile->filedes, vec, nvec)) < 0) {
+ *nbytes = 0;
+ rv = errno;
+ }
+ else {
+ *nbytes = bytes;
+ rv = APR_SUCCESS;
+ }
+ return rv;
+#else
+ /**
+ * The problem with trying to output the entire iovec is that we cannot
+ * maintain the behaviour that a real writev would have. If we iterate
+ * over the iovec one at a time, we lose the atomic properties of
+ * writev(). The other option is to combine the entire iovec into one
+ * buffer that we could then send in one call to write(). This is not
+ * reasonable since we do not know how much data an iovec could contain.
+ *
+ * The only reasonable option, that maintains the semantics of a real
+ * writev(), is to only write the first iovec. Callers of file_writev()
+ * must deal with partial writes as they normally would. If you want to
+ * ensure an entire iovec is written, use apr_file_writev_full().
+ */
+
+ *nbytes = vec[0].iov_len;
+ return apr_file_write(thefile, vec[0].iov_base, nbytes);
+#endif
+}
+
+APR_DECLARE(apr_status_t) apr_file_putc(char ch, apr_file_t *thefile)
+{
+ apr_size_t nbytes = 1;
+
+ return apr_file_write(thefile, &ch, &nbytes);
+}
+
+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_size_t nbytes = 1;
+
+ return apr_file_read(thefile, ch, &nbytes);
+}
+
+APR_DECLARE(apr_status_t) apr_file_puts(const char *str, apr_file_t *thefile)
+{
+ return apr_file_write_full(thefile, str, strlen(str), NULL);
+}
+
+apr_status_t apr_file_flush_locked(apr_file_t *thefile)
+{
+ apr_status_t rv = APR_SUCCESS;
+
+ if (thefile->direction == 1 && thefile->bufpos) {
+ apr_ssize_t written = 0, ret;
+
+ do {
+ ret = write(thefile->filedes, thefile->buffer + written,
+ thefile->bufpos - written);
+ if (ret > 0)
+ written += ret;
+ } while (written < thefile->bufpos &&
+ (ret > 0 || (ret == -1 && errno == EINTR)));
+ if (ret == -1) {
+ rv = errno;
+ } else {
+ thefile->filePtr += written;
+ thefile->bufpos = 0;
+ }
+ }
+
+ return rv;
+}
+
+APR_DECLARE(apr_status_t) apr_file_flush(apr_file_t *thefile)
+{
+ apr_status_t rv = APR_SUCCESS;
+
+ if (thefile->buffered) {
+ file_lock(thefile);
+ rv = apr_file_flush_locked(thefile);
+ file_unlock(thefile);
+ }
+ /* There isn't anything to do if we aren't buffering the output
+ * so just return success.
+ */
+ return rv;
+}
+
+APR_DECLARE(apr_status_t) apr_file_sync(apr_file_t *thefile)
+{
+ apr_status_t rv = APR_SUCCESS;
+
+ file_lock(thefile);
+
+ if (thefile->buffered) {
+ rv = apr_file_flush_locked(thefile);
+
+ if (rv != APR_SUCCESS) {
+ file_unlock(thefile);
+ return rv;
+ }
+ }
+
+ if (fsync(thefile->filedes)) {
+ rv = apr_get_os_error();
+ }
+
+ file_unlock(thefile);
+
+ return rv;
+}
+
+APR_DECLARE(apr_status_t) apr_file_datasync(apr_file_t *thefile)
+{
+ apr_status_t rv = APR_SUCCESS;
+ int os_status = 0;
+
+ file_lock(thefile);
+
+ if (thefile->buffered) {
+ rv = apr_file_flush_locked(thefile);
+
+ if (rv != APR_SUCCESS) {
+ file_unlock(thefile);
+ return rv;
+ }
+ }
+
+#ifdef HAVE_FDATASYNC
+ os_status = fdatasync(thefile->filedes);
+#elif defined(F_FULLFSYNC)
+ os_status = fcntl(thefile->filedes, F_FULLFSYNC);
+ if (os_status) {
+ /* Fall back to fsync() if the device doesn't support F_FULLFSYNC. */
+ os_status = fsync(thefile->filedes);
+ }
+#else
+ os_status = fsync(thefile->filedes);
+#endif
+ if (os_status) {
+ rv = apr_get_os_error();
+ }
+
+ file_unlock(thefile);
+
+ return rv;
+}
+
+APR_DECLARE(apr_status_t) apr_file_gets(char *str, int len, apr_file_t *thefile)
+{
+ apr_status_t rv = APR_SUCCESS; /* get rid of gcc warning */
+ apr_size_t nbytes;
+ const char *str_start = str;
+ char *final = str + len - 1;
+
+ if (len <= 1) {
+ /* sort of like fgets(), which returns NULL and stores no bytes
+ */
+ return APR_SUCCESS;
+ }
+
+ /* If we have an underlying buffer, we can be *much* more efficient
+ * and skip over the apr_file_read calls.
+ */
+ if (thefile->buffered) {
+ file_lock(thefile);
+
+ if (thefile->direction == 1) {
+ rv = apr_file_flush_locked(thefile);
+ if (rv) {
+ file_unlock(thefile);
+ return rv;
+ }
+
+ thefile->direction = 0;
+ thefile->bufpos = 0;
+ thefile->dataRead = 0;
+ }
+
+ while (str < final) { /* leave room for trailing '\0' */
+ /* Force ungetc leftover to call apr_file_read. */
+ if (thefile->bufpos < thefile->dataRead &&
+ thefile->ungetchar == -1) {
+ *str = thefile->buffer[thefile->bufpos++];
+ }
+ else {
+ nbytes = 1;
+ rv = file_read_buffered(thefile, str, &nbytes);
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ }
+ if (*str == '\n') {
+ ++str;
+ break;
+ }
+ ++str;
+ }
+ file_unlock(thefile);
+ }
+ else {
+ while (str < final) { /* leave room for trailing '\0' */
+ nbytes = 1;
+ rv = apr_file_read(thefile, str, &nbytes);
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ if (*str == '\n') {
+ ++str;
+ break;
+ }
+ ++str;
+ }
+ }
+
+ /* We must store a terminating '\0' if we've stored any chars. We can
+ * get away with storing it if we hit an error first.
+ */
+ *str = '\0';
+ if (str > str_start) {
+ /* we stored chars; don't report EOF or any other errors;
+ * the app will find out about that on the next call
+ */
+ return APR_SUCCESS;
+ }
+ return rv;
+}
+
+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;
+
+ /* don't really need a HUGE_STRING_LEN anymore */
+ data.buf = malloc(HUGE_STRING_LEN);
+ if (data.buf == NULL) {
+ return -1;
+ }
+ 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;
+}
diff --git a/file_io/unix/seek.c b/file_io/unix/seek.c
new file mode 100644
index 0000000..2e97337
--- /dev/null
+++ b/file_io/unix/seek.c
@@ -0,0 +1,136 @@
+/* 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"
+
+static apr_status_t setptr(apr_file_t *thefile, apr_off_t pos )
+{
+ apr_off_t newbufpos;
+ apr_status_t rv;
+
+ if (thefile->direction == 1) {
+ rv = apr_file_flush_locked(thefile);
+ if (rv) {
+ return rv;
+ }
+ thefile->bufpos = thefile->direction = thefile->dataRead = 0;
+ }
+
+ newbufpos = pos - (thefile->filePtr - thefile->dataRead);
+ if (newbufpos >= 0 && newbufpos <= thefile->dataRead) {
+ thefile->bufpos = newbufpos;
+ rv = APR_SUCCESS;
+ }
+ else {
+ if (lseek(thefile->filedes, pos, SEEK_SET) != -1) {
+ thefile->bufpos = thefile->dataRead = 0;
+ thefile->filePtr = pos;
+ rv = APR_SUCCESS;
+ }
+ else {
+ rv = errno;
+ }
+ }
+
+ return rv;
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_seek(apr_file_t *thefile, apr_seek_where_t where, apr_off_t *offset)
+{
+ apr_off_t rv;
+
+ thefile->eof_hit = 0;
+
+ if (thefile->buffered) {
+ int rc = EINVAL;
+ apr_finfo_t finfo;
+
+ file_lock(thefile);
+
+ 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_locked(&finfo, APR_FINFO_SIZE, thefile);
+ if (rc == APR_SUCCESS)
+ rc = setptr(thefile, finfo.size + *offset);
+ break;
+ }
+
+ *offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
+
+ file_unlock(thefile);
+
+ return rc;
+ }
+ else {
+ rv = lseek(thefile->filedes, *offset, where);
+ if (rv == -1) {
+ *offset = -1;
+ return errno;
+ }
+ else {
+ *offset = rv;
+ return APR_SUCCESS;
+ }
+ }
+}
+
+apr_status_t apr_file_trunc(apr_file_t *fp, apr_off_t offset)
+{
+ if (fp->buffered) {
+ int rc = 0;
+ file_lock(fp);
+ if (fp->direction == 1 && fp->bufpos != 0) {
+ apr_off_t len = fp->filePtr + fp->bufpos;
+ if (offset < len) {
+ /* New file end fall below our write buffer limit.
+ * Figure out if and what needs to be flushed.
+ */
+ apr_off_t off = len - offset;
+ if (off >= 0 && off <= fp->bufpos)
+ fp->bufpos = fp->bufpos - (size_t)off;
+ else
+ fp->bufpos = 0;
+ }
+ rc = apr_file_flush_locked(fp);
+ /* Reset buffer positions for write mode */
+ fp->bufpos = fp->direction = fp->dataRead = 0;
+ }
+ else if (fp->direction == 0) {
+ /* Discard the read buffer, as we are about to reposition
+ * ourselves to the end of file.
+ */
+ fp->bufpos = 0;
+ fp->dataRead = 0;
+ }
+ file_unlock(fp);
+ if (rc) {
+ return rc;
+ }
+ }
+ if (ftruncate(fp->filedes, offset) == -1) {
+ return errno;
+ }
+ return apr_file_seek(fp, APR_SET, &offset);
+}
diff --git a/file_io/unix/tempdir.c b/file_io/unix/tempdir.c
new file mode 100644
index 0000000..22325ef
--- /dev/null
+++ b/file_io/unix/tempdir.c
@@ -0,0 +1,129 @@
+/* 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_file_io.h"
+#include "apr_strings.h"
+#include "apr_env.h"
+
+
+/* Try to open a temporary file in the temporary dir, write to it,
+ and then close it. */
+static int test_tempdir(const char *temp_dir, apr_pool_t *p)
+{
+ apr_file_t *dummy_file;
+ char *path = apr_pstrcat(p, temp_dir, "/apr-tmp.XXXXXX", NULL);
+
+ if (apr_file_mktemp(&dummy_file, path, 0, p) == APR_SUCCESS) {
+ if (apr_file_putc('!', dummy_file) == APR_SUCCESS) {
+ if (apr_file_close(dummy_file) == APR_SUCCESS) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+APR_DECLARE(apr_status_t) apr_temp_dir_get(const char **temp_dir,
+ apr_pool_t *p)
+{
+ apr_status_t apr_err;
+ const char *try_dirs[] = { "/tmp", "/usr/tmp", "/var/tmp" };
+ const char *try_envs[] = { "TMPDIR", "TMP", "TEMP"};
+ const char *dir;
+ char *cwd;
+ int i;
+
+ /* Our goal is to find a temporary directory suitable for writing
+ into.
+ Here's the order in which we'll try various paths:
+
+ $TMPDIR
+ $TMP
+ $TEMP
+ "C:\TEMP" (windows only)
+ "SYS:\TMP" (netware only)
+ "/tmp"
+ "/var/tmp"
+ "/usr/tmp"
+ P_tmpdir (POSIX define)
+ `pwd`
+
+ NOTE: This algorithm is basically the same one used by Python
+ 2.2's tempfile.py module. */
+
+ /* Try the environment first. */
+ for (i = 0; i < (sizeof(try_envs) / sizeof(const char *)); i++) {
+ char *value;
+ apr_err = apr_env_get(&value, try_envs[i], p);
+ if ((apr_err == APR_SUCCESS) && value) {
+ apr_size_t len = strlen(value);
+ if (len && (len < APR_PATH_MAX) && test_tempdir(value, p)) {
+ dir = value;
+ goto end;
+ }
+ }
+ }
+
+#ifdef WIN32
+ /* Next, on Win32, try the C:\TEMP directory. */
+ if (test_tempdir("C:\\TEMP", p)) {
+ dir = "C:\\TEMP";
+ goto end;
+ }
+#endif
+#ifdef NETWARE
+ /* Next, on NetWare, try the SYS:/TMP directory. */
+ if (test_tempdir("SYS:/TMP", p)) {
+ dir = "SYS:/TMP";
+ goto end;
+ }
+#endif
+
+ /* Next, try a set of hard-coded paths. */
+ for (i = 0; i < (sizeof(try_dirs) / sizeof(const char *)); i++) {
+ if (test_tempdir(try_dirs[i], p)) {
+ dir = try_dirs[i];
+ goto end;
+ }
+ }
+
+#ifdef P_tmpdir
+ /*
+ * If we have it, use the POSIX definition of where
+ * the tmpdir should be
+ */
+ if (test_tempdir(P_tmpdir, p)) {
+ dir = P_tmpdir;
+ goto end;
+ }
+#endif
+
+ /* Finally, try the current working directory. */
+ if (APR_SUCCESS == apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, p)) {
+ if (test_tempdir(cwd, p)) {
+ dir = cwd;
+ goto end;
+ }
+ }
+
+ /* We didn't find a suitable temp dir anywhere */
+ return APR_EGENERAL;
+
+end:
+ *temp_dir = apr_pstrdup(p, dir);
+ return APR_SUCCESS;
+}
diff --git a/file_io/win32/buffer.c b/file_io/win32/buffer.c
new file mode 100644
index 0000000..7f5d9d3
--- /dev/null
+++ b/file_io/win32/buffer.c
@@ -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;
+}
diff --git a/file_io/win32/dir.c b/file_io/win32/dir.c
new file mode 100644
index 0000000..8c8b745
--- /dev/null
+++ b/file_io/win32/dir.c
@@ -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, 1, fname, 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;
+}
diff --git a/file_io/win32/filedup.c b/file_io/win32/filedup.c
new file mode 100644
index 0000000..6627864
--- /dev/null
+++ b/file_io/win32/filedup.c
@@ -0,0 +1,228 @@
+/* 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_kill(old_file->pool, (void *)old_file,
+ file_cleanup);
+ apr_pool_cleanup_register(p, (void *)(*new_file),
+ file_cleanup,
+ file_cleanup);
+ }
+
+ old_file->filehand = INVALID_HANDLE_VALUE;
+#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;
+}
diff --git a/file_io/win32/filepath.c b/file_io/win32/filepath.c
new file mode 100644
index 0000000..5dbe5ae
--- /dev/null
+++ b/file_io/win32/filepath.c
@@ -0,0 +1,1005 @@
+/* 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_private.h"
+#include "apr_arch_file_io.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include <string.h>
+#include <ctype.h>
+
+#ifdef NETWARE
+#include <unistd.h>
+#include <fsio.h>
+#endif
+
+ /* WinNT accepts several odd forms of a 'root' path. Under Unicode
+ * calls (ApiFunctionW) the //?/C:/foo or //?/UNC/mach/share/foo forms
+ * are accepted. Ansi and Unicode functions both accept the //./C:/foo
+ * form under WinNT/2K. Since these forms are handled in the utf-8 to
+ * unicode translation phase, we don't want the user confused by them, so
+ * we will accept them but always return the canonical C:/ or //mach/share/
+ *
+ * OS2 appears immune from the nonsense :)
+ */
+
+APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
+ const char **inpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ const char *testpath = *inpath;
+ char *newpath;
+#ifdef NETWARE
+ char seperator[2] = { 0, 0};
+ char server[APR_PATH_MAX+1];
+ char volume[APR_PATH_MAX+1];
+ char file[APR_PATH_MAX+1];
+ char *volsep = NULL;
+ int elements;
+
+ if (inpath && *inpath)
+ volsep = strchr (*inpath, ':');
+ else
+ return APR_EBADPATH;
+
+ if (strlen(*inpath) > APR_PATH_MAX) {
+ return APR_EBADPATH;
+ }
+
+ seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
+
+ /* Allocate and initialize each of the segment buffers
+ */
+ server[0] = volume[0] = file[0] = '\0';
+
+ /* If we don't have a volume separator then don't bother deconstructing
+ the path since we won't use the deconstructed information anyway.
+ */
+ if (volsep) {
+ /* Split the inpath into its separate parts. */
+ deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
+
+ /* If we got a volume part then continue splitting out the root.
+ Otherwise we either have an incomplete or relative path
+ */
+ if (volume && strlen(volume) > 0) {
+ newpath = apr_pcalloc(p, strlen(server)+strlen(volume)+5);
+ construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE);
+
+ /* NetWare doesn't add the root slash so we need to add it manually.
+ */
+ strcat(newpath, seperator);
+ *rootpath = newpath;
+
+ /* Skip the inpath pointer down to the first non-root character
+ */
+ newpath = volsep;
+ do {
+ ++newpath;
+ } while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
+ *inpath = newpath;
+
+ /* Need to handle APR_FILEPATH_TRUENAME checking here. */
+
+ return APR_SUCCESS;
+ }
+ else
+ return APR_EBADPATH;
+ }
+ else if ((**inpath == '/') || (**inpath == '\\')) {
+ /* if we have a root path without a volume then just split
+ in same manner as unix although this path will be
+ incomplete.
+ */
+ *rootpath = apr_pstrdup(p, seperator);
+ do {
+ ++(*inpath);
+ } while ((**inpath == '/') || (**inpath == '\\'));
+ }
+ else
+ return APR_ERELATIVE;
+
+ return APR_EINCOMPLETE;
+
+#else /* ndef(NETWARE) */
+
+ char seperator[2];
+ const char *delim1;
+ const char *delim2;
+
+ seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
+ seperator[1] = 0;
+
+ if (testpath[0] == '/' || testpath[0] == '\\') {
+ if (testpath[1] == '/' || testpath[1] == '\\') {
+
+#ifdef WIN32 /* //server/share isn't the only // delimited syntax */
+ if ((testpath[2] == '?' || testpath[2] == '.')
+ && (testpath[3] == '/' || testpath[3] == '\\')) {
+ if (IS_FNCHAR(testpath[4]) && testpath[5] == ':')
+ {
+ apr_status_t rv;
+ testpath += 4;
+ /* given '//?/C: or //./C: let us try this
+ * all over again from the drive designator
+ */
+ rv = apr_filepath_root(rootpath, &testpath, flags, p);
+ if (!rv || rv == APR_EINCOMPLETE)
+ *inpath = testpath;
+ return rv;
+ }
+ else if (strncasecmp(testpath + 4, "UNC", 3) == 0
+ && (testpath[7] == '/' || testpath[7] == '\\')
+ && (testpath[2] == '?')) {
+ /* given '//?/UNC/machine/share, a little magic
+ * at the end makes this all work out by using
+ * 'C/machine' as the starting point and replacing
+ * the UNC delimiters with \'s, including the 'C'
+ */
+ testpath += 6;
+ }
+ else
+ /* This must not be a path to a file, but rather
+ * a volume or device. Die for now.
+ */
+ return APR_EBADPATH;
+ }
+#endif /* WIN32 (non - //server/share syntax) */
+
+ /* Evaluate path of '//[machine/[share[/]]]' */
+ delim1 = testpath + 2;
+ do {
+ /* Protect against //X/ where X is illegal */
+ if (*delim1 && !IS_FNCHAR(*(delim1++)))
+ return APR_EBADPATH;
+ } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
+
+ if (*delim1) {
+ apr_status_t rv;
+ delim2 = delim1 + 1;
+ while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
+ /* Protect against //machine/X/ where X is illegal */
+ if (!IS_FNCHAR(*(delim2++)))
+ return APR_EBADPATH;
+ }
+
+ /* Copy the '//machine/[share[/]]' path, always providing
+ * an extra byte for the trailing slash.
+ */
+ newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1);
+
+ if (delim2 == delim1 + 1) {
+ /* We found simply \\machine\, so give up already
+ */
+ *rootpath = newpath;
+ *inpath = delim2;
+ return APR_EINCOMPLETE;
+ }
+
+ if (flags & APR_FILEPATH_TRUENAME) {
+ /* Validate the \\Machine\Share\ designation,
+ * Win32 will argue about slashed in UNC paths,
+ * so use backslashes till we finish testing,
+ * and add the trailing backslash [required].
+ * apr_pstrmemdup above guarentees us the new
+ * trailing null character.
+ */
+ newpath[0] = '\\';
+ newpath[1] = '\\';
+ newpath[delim1 - testpath] = '\\';
+ newpath[delim2 - testpath] = '\\';
+
+ rv = filepath_root_test(newpath, p);
+ if (rv)
+ return rv;
+ rv = filepath_root_case(&newpath, newpath, p);
+ if (rv)
+ return rv;
+ newpath[0] = seperator[0];
+ newpath[1] = seperator[0];
+ newpath[delim1 - testpath] = seperator[0];
+ newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
+ }
+ else {
+ /* Give back the caller's own choice of delimiters
+ */
+ newpath[0] = testpath[0];
+ newpath[1] = testpath[1];
+ newpath[delim1 - testpath] = *delim1;
+ newpath[delim2 - testpath] = *delim2;
+ }
+
+ /* If this root included the trailing / or \ designation
+ * then lop off multiple trailing slashes and give back
+ * appropriate delimiters.
+ */
+ if (*delim2) {
+ *inpath = delim2 + 1;
+ while (**inpath == '/' || **inpath == '\\')
+ ++*inpath;
+ }
+ else {
+ *inpath = delim2;
+ }
+
+ *rootpath = newpath;
+ return APR_SUCCESS;
+ }
+
+ /* Have path of '\\[machine]', if the machine is given,
+ * append same trailing slash as the leading slash
+ */
+ delim1 = strchr(testpath, '\0');
+ if (delim1 > testpath + 2) {
+ newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1);
+ if (flags & APR_FILEPATH_TRUENAME)
+ newpath[delim1 - testpath] = seperator[0];
+ else
+ newpath[delim1 - testpath] = newpath[0];
+ newpath[delim1 - testpath + 1] = '\0';
+ }
+ else {
+ newpath = apr_pstrndup(p, testpath, delim1 - testpath);
+ }
+ if (flags & APR_FILEPATH_TRUENAME) {
+ newpath[0] = seperator[0];
+ newpath[1] = seperator[0];
+ }
+ *rootpath = newpath;
+ *inpath = delim1;
+ return APR_EINCOMPLETE;
+ }
+
+ /* Left with a path of '/', what drive are we asking about?
+ */
+ *inpath = testpath + 1;
+ newpath = apr_palloc(p, 2);
+ if (flags & APR_FILEPATH_TRUENAME)
+ newpath[0] = seperator[0];
+ else
+ newpath[0] = testpath[0];
+ newpath[1] = '\0';
+ *rootpath = newpath;
+ return APR_EINCOMPLETE;
+ }
+
+ /* Evaluate path of 'd:[/]' */
+ if (IS_FNCHAR(*testpath) && testpath[1] == ':')
+ {
+ apr_status_t rv;
+ /* Validate that D:\ drive exists, test must be rooted
+ * Note that posix/win32 insists a drive letter is upper case,
+ * so who are we to argue with a 'feature'.
+ * It is a safe fold, since only A-Z is legal, and has no
+ * side effects of legal mis-mapped non-us-ascii codes.
+ */
+ newpath = apr_palloc(p, 4);
+ newpath[0] = testpath[0];
+ newpath[1] = testpath[1];
+ newpath[2] = seperator[0];
+ newpath[3] = '\0';
+ if (flags & APR_FILEPATH_TRUENAME) {
+ newpath[0] = apr_toupper(newpath[0]);
+ rv = filepath_root_test(newpath, p);
+ if (rv)
+ return rv;
+ }
+ /* Just give back the root the user handed to us.
+ */
+ if (testpath[2] != '/' && testpath[2] != '\\') {
+ newpath[2] = '\0';
+ *rootpath = newpath;
+ *inpath = testpath + 2;
+ return APR_EINCOMPLETE;
+ }
+
+ /* strip off remaining slashes that designate the root,
+ * give the caller back their original choice of slash
+ * unless this is TRUENAME'ed
+ */
+ *inpath = testpath + 3;
+ while (**inpath == '/' || **inpath == '\\')
+ ++*inpath;
+ if (!(flags & APR_FILEPATH_TRUENAME))
+ newpath[2] = testpath[2];
+ *rootpath = newpath;
+ return APR_SUCCESS;
+ }
+
+ /* Nothing interesting */
+ return APR_ERELATIVE;
+
+#endif /* ndef(NETWARE) */
+}
+
+#if !defined(NETWARE)
+static int same_drive(const char *path1, const char *path2)
+{
+ char drive1 = path1[0];
+ char drive2 = path2[0];
+
+ if (!drive1 || !drive2 || path1[1] != ':' || path2[1] != ':')
+ return FALSE;
+
+ if (drive1 == drive2)
+ return TRUE;
+
+ if (drive1 >= 'a' && drive1 <= 'z')
+ drive1 += 'A' - 'a';
+
+ if (drive2 >= 'a' && drive2 <= 'z')
+ drive2 += 'A' - 'a';
+
+ return (drive1 == drive2);
+}
+#endif
+
+APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
+ const char *basepath,
+ const char *addpath,
+ apr_int32_t flags,
+ apr_pool_t *p)
+{
+ char path[APR_PATH_MAX]; /* isn't null term */
+ const char *baseroot = NULL;
+ const char *addroot;
+ apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
+ apr_size_t baselen; /* the length of basepath (excluding baseroot) */
+ apr_size_t keptlen; /* the length of the retained basepath (incl root) */
+ apr_size_t pathlen; /* the length of the result path */
+ apr_size_t segend; /* the end of the current segment */
+ apr_size_t seglen; /* the length of the segment (excl trailing chars) */
+ apr_status_t basetype = 0; /* from parsing the basepath's baseroot */
+ apr_status_t addtype; /* from parsing the addpath's addroot */
+ apr_status_t rv;
+#ifndef NETWARE
+ int fixunc = 0; /* flag to complete an incomplete UNC basepath */
+#endif
+
+ /* Treat null as an empty path, otherwise split addroot from the addpath
+ */
+ if (!addpath) {
+ addpath = addroot = "";
+ addtype = APR_ERELATIVE;
+ }
+ else {
+ /* This call _should_ test the path
+ */
+ addtype = apr_filepath_root(&addroot, &addpath,
+ APR_FILEPATH_TRUENAME
+ | (flags & APR_FILEPATH_NATIVE),
+ p);
+ if (addtype == APR_SUCCESS) {
+ addtype = APR_EABSOLUTE;
+ }
+ else if (addtype == APR_ERELATIVE) {
+ addroot = "";
+ }
+ else if (addtype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was incomprehensible so fail already
+ */
+ return addtype;
+ }
+ }
+
+ /* If addpath is (even partially) rooted, then basepath is
+ * unused. Ths violates any APR_FILEPATH_SECUREROOTTEST
+ * and APR_FILEPATH_NOTABSOLUTE flags specified.
+ */
+ if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
+ {
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+ if (flags & APR_FILEPATH_NOTABSOLUTE)
+ return addtype;
+ }
+
+ /* Optimized tests before we query the current working path
+ */
+ if (!basepath) {
+
+ /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
+ * we won't test the root again, it's ignored.
+ * Waste no CPU retrieving the working path.
+ */
+ if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
+ basepath = baseroot = "";
+ basetype = APR_ERELATIVE;
+ }
+
+ /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
+ * requires an absolutely relative result, So do not retrieve
+ * the working path.
+ */
+ if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
+ basepath = baseroot = "";
+ basetype = APR_ERELATIVE;
+ }
+ }
+
+ if (!basepath)
+ {
+ /* Start with the current working path. This is bass akwards,
+ * but required since the compiler (at least vc) doesn't like
+ * passing the address of a char const* for a char** arg.
+ * We must grab the current path of the designated drive
+ * if addroot is given in drive-relative form (e.g. d:foo)
+ */
+ char *getpath;
+#ifndef NETWARE
+ if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
+ rv = filepath_drive_get(&getpath, addroot[0], flags, p);
+ else
+#endif
+ rv = apr_filepath_get(&getpath, flags, p);
+ if (rv != APR_SUCCESS)
+ return rv;
+ basepath = getpath;
+ }
+
+ if (!baseroot) {
+ /* This call should _not_ test the path
+ */
+ basetype = apr_filepath_root(&baseroot, &basepath,
+ (flags & APR_FILEPATH_NATIVE), p);
+ if (basetype == APR_SUCCESS) {
+ basetype = APR_EABSOLUTE;
+ }
+ else if (basetype == APR_ERELATIVE) {
+ baseroot = "";
+ }
+ else if (basetype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was incomprehensible so fail already
+ */
+ return basetype;
+ }
+ }
+ baselen = strlen(basepath);
+
+ /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
+ * requires an absolutely relative result. If the given
+ * basepath is not relative then fail.
+ */
+ if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
+ return basetype;
+
+ /* The Win32 nightmare on unc street... start combining for
+ * many possible root combinations.
+ */
+ if (addtype == APR_EABSOLUTE)
+ {
+ /* Ignore the given root path, and start with the addroot
+ */
+ if ((flags & APR_FILEPATH_NOTABOVEROOT)
+ && strncmp(baseroot, addroot, strlen(baseroot)))
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ else if (addtype == APR_EINCOMPLETE)
+ {
+ /* There are several types of incomplete paths,
+ * incomplete UNC paths (//foo/ or //),
+ * drives without rooted paths (d: as in d:foo),
+ * and simple roots (/ as in /foo).
+ * Deal with these in significantly different manners...
+ */
+#ifndef NETWARE
+ if ((addroot[0] == '/' || addroot[0] == '\\') &&
+ (addroot[1] == '/' || addroot[1] == '\\'))
+ {
+ /* Ignore the given root path if the incomplete addpath is UNC,
+ * (note that the final result will be incomplete).
+ */
+ if (flags & APR_FILEPATH_NOTRELATIVE)
+ return addtype;
+ if ((flags & APR_FILEPATH_NOTABOVEROOT)
+ && strncmp(baseroot, addroot, strlen(baseroot)))
+ return APR_EABOVEROOT;
+ fixunc = 1;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ else
+#endif
+ if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1])
+ {
+ /* Bring together the drive or UNC root from the baseroot
+ * if the addpath is a simple root and basepath is rooted,
+ * otherwise disregard the basepath entirely.
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+ if (basetype != APR_ERELATIVE) {
+#ifndef NETWARE
+ if (basetype == APR_INCOMPLETE
+ && (baseroot[0] == '/' || baseroot[0] == '\\')
+ && (baseroot[1] == '/' || baseroot[1] == '\\'))
+ fixunc = 1;
+#endif
+ keptlen = rootlen = pathlen = strlen(baseroot);
+ memcpy(path, baseroot, pathlen);
+ }
+ else {
+ if (flags & APR_FILEPATH_NOTABOVEROOT)
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ }
+#ifdef NETWARE
+ else if (filepath_has_drive(addroot, DRIVE_ONLY, p))
+ {
+ /* If the addroot is a drive (without a volume root)
+ * use the basepath _if_ it matches this drive letter!
+ * Otherwise we must discard the basepath.
+ */
+ if (!filepath_compare_drive(addroot, baseroot, p) &&
+ filepath_has_drive(baseroot, 0, p)) {
+#else
+ else if (addroot[0] && addroot[1] == ':' && !addroot[2])
+ {
+ /* If the addroot is a drive (without a volume root)
+ * use the basepath _if_ it matches this drive letter!
+ * Otherwise we must discard the basepath.
+ */
+ if (same_drive(addroot, baseroot)) {
+#endif
+ /* Base the result path on the basepath
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+ rootlen = strlen(baseroot);
+ keptlen = pathlen = rootlen + baselen;
+ if (keptlen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path, baseroot, rootlen);
+ memcpy(path + rootlen, basepath, baselen);
+ }
+ else {
+ if (flags & APR_FILEPATH_NOTRELATIVE)
+ return addtype;
+ if (flags & APR_FILEPATH_NOTABOVEROOT)
+ return APR_EABOVEROOT;
+ keptlen = 0;
+ rootlen = pathlen = strlen(addroot);
+ memcpy(path, addroot, pathlen);
+ }
+ }
+ else {
+ /* Now this is unexpected, we aren't aware of any other
+ * incomplete path forms! Fail now.
+ */
+ return APR_EBADPATH;
+ }
+ }
+ else { /* addtype == APR_ERELATIVE */
+ /* If both paths are relative, fail early
+ */
+ if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
+ return basetype;
+
+#ifndef NETWARE
+ /* An incomplete UNC path must be completed
+ */
+ if (basetype == APR_INCOMPLETE
+ && (baseroot[0] == '/' || baseroot[0] == '\\')
+ && (baseroot[1] == '/' || baseroot[1] == '\\'))
+ fixunc = 1;
+#endif
+
+ /* Base the result path on the basepath
+ */
+ rootlen = strlen(baseroot);
+ keptlen = pathlen = rootlen + baselen;
+ if (keptlen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path, baseroot, rootlen);
+ memcpy(path + rootlen, basepath, baselen);
+ }
+
+ /* '/' terminate the given root path unless it's already terminated
+ * or is an incomplete drive root. Correct the trailing slash unless
+ * we have an incomplete UNC path still to fix.
+ */
+ if (pathlen && path[pathlen - 1] != ':') {
+ if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
+ if (pathlen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+
+ path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
+ }
+ /* XXX: wrong, but gotta figure out what I intended;
+ * else if (!fixunc)
+ * path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
+ */
+ }
+
+ while (*addpath)
+ {
+ /* Parse each segment, find the closing '/'
+ */
+ seglen = 0;
+ while (addpath[seglen] && addpath[seglen] != '/'
+ && addpath[seglen] != '\\')
+ ++seglen;
+
+ /* Truncate all trailing spaces and all but the first two dots */
+ segend = seglen;
+ while (seglen && (addpath[seglen - 1] == ' '
+ || addpath[seglen - 1] == '.')) {
+ if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
+ --seglen;
+ else
+ break;
+ }
+
+ if (seglen == 0 || (seglen == 1 && addpath[0] == '.'))
+ {
+ /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
+ * so eliminate all preconceptions that it is valid.
+ */
+ if (seglen < segend)
+ return APR_EBADPATH;
+
+#ifndef NETWARE
+ /* This isn't legal unless the unc path is completed
+ */
+ if (fixunc)
+ return APR_EBADPATH;
+#endif
+
+ /* Otherwise, this is a noop segment (/ or ./) so ignore it
+ */
+ }
+ else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')
+ {
+ /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
+ * and '/..../', some functions treat it as ".", and some
+ * fail! Eliminate all preconceptions that they are valid.
+ */
+ if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
+ return APR_EBADPATH;
+
+#ifndef NETWARE
+ /* This isn't legal unless the unc path is completed
+ */
+ if (fixunc)
+ return APR_EBADPATH;
+#endif
+
+ /* backpath (../) when an absolute path is given */
+ if (rootlen && (pathlen <= rootlen))
+ {
+ /* Attempt to move above root. Always die if the
+ * APR_FILEPATH_SECUREROOTTEST flag is specified.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+
+ /* Otherwise this is simply a noop, above root is root.
+ */
+ }
+ else if (pathlen == 0
+ || (pathlen >= 3
+ && (pathlen == 3
+ || path[pathlen - 4] == ':'
+ || path[pathlen - 4] == '/'
+ || path[pathlen - 4] == '\\')
+ && path[pathlen - 3] == '.'
+ && path[pathlen - 2] == '.'
+ && (path[pathlen - 1] == '/'
+ || path[pathlen - 1] == '\\')))
+ {
+ /* Verified path is empty, exactly "..[/\]", or ends
+ * in "[:/\]..[/\]" - these patterns we will not back
+ * over since they aren't 'prior segements'.
+ *
+ * If APR_FILEPATH_SECUREROOTTEST.was given, die now.
+ */
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+
+ /* Otherwise append another backpath.
+ */
+ if (pathlen + 3 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ path[pathlen++] = '.';
+ path[pathlen++] = '.';
+ if (addpath[segend]) {
+ path[pathlen++] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : ((flags & APR_FILEPATH_TRUENAME)
+ ? '/' : addpath[segend]));
+ }
+ /* The 'root' part of this path now includes the ../ path,
+ * because that backpath will not be parsed by the truename
+ * code below.
+ */
+ keptlen = pathlen;
+ }
+ else
+ {
+ /* otherwise crop the prior segment
+ */
+ do {
+ --pathlen;
+ } while (pathlen && path[pathlen - 1] != '/'
+ && path[pathlen - 1] != '\\');
+
+ /* Now test if we are above where we started and back up
+ * the keptlen offset to reflect the added/altered path.
+ */
+ if (pathlen < keptlen)
+ {
+ if (flags & APR_FILEPATH_SECUREROOTTEST)
+ return APR_EABOVEROOT;
+ keptlen = pathlen;
+ }
+ }
+ }
+ else /* not empty or dots */
+ {
+#ifndef NETWARE
+ if (fixunc) {
+ const char *testpath = path;
+ const char *testroot;
+ apr_status_t testtype;
+ apr_size_t i = (addpath[segend] != '\0');
+
+ /* This isn't legal unless the unc path is complete!
+ */
+ if (seglen < segend)
+ return APR_EBADPATH;
+ if (pathlen + seglen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path + pathlen, addpath, seglen + i);
+
+ /* Always add the trailing slash to a UNC segment
+ */
+ path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : '/');
+ pathlen += seglen + 1;
+
+ /* Recanonicalize the UNC root with the new UNC segment,
+ * and if we succeed, reset this test and the rootlen,
+ * and replace our path with the canonical UNC root path
+ */
+ path[pathlen] = '\0';
+ /* This call _should_ test the path
+ */
+ testtype = apr_filepath_root(&testroot, &testpath,
+ APR_FILEPATH_TRUENAME
+ | (flags & APR_FILEPATH_NATIVE),
+ p);
+ if (testtype == APR_SUCCESS) {
+ rootlen = pathlen = (testpath - path);
+ memcpy(path, testroot, pathlen);
+ fixunc = 0;
+ }
+ else if (testtype != APR_EINCOMPLETE) {
+ /* apr_filepath_root was very unexpected so fail already
+ */
+ return testtype;
+ }
+ }
+ else
+#endif
+ {
+ /* An actual segment, append it to the destination path
+ */
+ apr_size_t i = (addpath[segend] != '\0');
+ if (pathlen + seglen + i >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memcpy(path + pathlen, addpath, seglen + i);
+ if (i)
+ path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
+ ? '\\' : '/');
+ pathlen += seglen + i;
+ }
+ }
+
+ /* Skip over trailing slash to the next segment
+ */
+ if (addpath[segend])
+ ++segend;
+
+ addpath += segend;
+ }
+
+ /* keptlen will be the baselen unless the addpath contained
+ * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT
+ * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
+ * compare the string beyond the root to assure the result path
+ * is still within given basepath. Note that the root path
+ * segment is thoroughly tested prior to path parsing.
+ */
+ if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
+ if (memcmp(basepath, path + rootlen, baselen) != 0)
+ return APR_EABOVEROOT;
+
+ /* Ahem... if we have a basepath without a trailing slash,
+ * we better be sure that /foo wasn't replaced with /foobar!
+ */
+ if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
+ && path[rootlen + baselen] && path[rootlen + baselen] != '/'
+ && path[rootlen + baselen] != '\\')
+ return APR_EABOVEROOT;
+ }
+
+ if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
+ /* We can always skip the root, it's already true-named. */
+ if (rootlen > keptlen)
+ keptlen = rootlen;
+ if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
+ /* By rights, keptlen may grown longer than pathlen.
+ * we wont' use it again (in that case) so we don't care.
+ */
+ ++keptlen;
+ }
+ /* Go through all the new segments */
+ while (keptlen < pathlen) {
+ apr_finfo_t finfo;
+ char saveslash = 0;
+ seglen = 0;
+ /* find any slash and set it aside for a minute. */
+ for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
+ if ((path[keptlen + seglen] == '/') ||
+ (path[keptlen + seglen] == '\\')) {
+ saveslash = path[keptlen + seglen];
+ break;
+ }
+ }
+ /* Null term for stat! */
+ path[keptlen + seglen] = '\0';
+ if ((rv = apr_stat(&finfo, path,
+ APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
+ == APR_SUCCESS) {
+ apr_size_t namelen = strlen(finfo.name);
+
+#if defined(OS2) /* only has case folding, never aliases that change the length */
+
+ if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
+ memcpy(path + keptlen, finfo.name, namelen);
+ }
+#else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
+
+ if ((namelen != seglen) ||
+ (memcmp(finfo.name, path + keptlen, seglen) != 0))
+ {
+ if (namelen <= seglen) {
+ memcpy(path + keptlen, finfo.name, namelen);
+ if ((namelen < seglen) && saveslash) {
+ memmove(path + keptlen + namelen + 1,
+ path + keptlen + seglen + 1,
+ pathlen - keptlen - seglen);
+ }
+ pathlen += namelen - seglen;
+ seglen = namelen;
+ }
+ else { /* namelen > seglen */
+ if (pathlen + namelen - seglen >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ if (saveslash) {
+ memmove(path + keptlen + namelen + 1,
+ path + keptlen + seglen + 1,
+ pathlen - keptlen - seglen);
+ }
+ memcpy(path + keptlen, finfo.name, namelen);
+ pathlen += namelen - seglen;
+ seglen = namelen;
+ }
+ }
+#endif /* !OS2 (Whatever that alias was we're over it) */
+
+ /* That's it, the rest is path info.
+ * I don't know how we aught to handle this. Should
+ * we define a new error to indicate 'more info'?
+ * Should we split out the rest of the path?
+ */
+ if ((finfo.filetype != APR_DIR) &&
+ (finfo.filetype != APR_LNK) && saveslash)
+ rv = APR_ENOTDIR;
+#ifdef XXX_FIGURE_THIS_OUT
+ {
+ /* the example inserts a null between the end of
+ * the filename and the next segment, and increments
+ * the path length so we would return both segments.
+ */
+ if (saveslash) {
+ keptlen += seglen;
+ path[keptlen] = saveslash;
+ if (pathlen + 1 >= sizeof(path))
+ return APR_ENAMETOOLONG;
+ memmove(path + keptlen + 1,
+ path + keptlen,
+ pathlen - keptlen);
+ path[keptlen] = '\0';
+ ++pathlen;
+ break;
+ }
+ }
+#endif
+ }
+
+ /* put back the '/' */
+ if (saveslash) {
+ path[keptlen + seglen] = saveslash;
+ ++seglen;
+ }
+ keptlen += seglen;
+
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_ENOENT(rv))
+ break;
+ if (APR_STATUS_IS_EPATHWILD(rv))
+ /* This path included wildcards. The path elements
+ * that did not contain wildcards are canonicalized,
+ * so we will return the path, although later elements
+ * don't necessarily exist, and aren't canonical.
+ */
+ break;
+ else if (APR_STATUS_IS_ENOTDIR(rv))
+ /* This is a little more serious, we just added a name
+ * onto a filename (think http's PATH_INFO)
+ * If the caller is foolish enough to do this, we expect
+ * the've already canonicalized the root) that they knew
+ * what they are doing :(
+ */
+ break;
+ else
+ return rv;
+ }
+ }
+ }
+
+ *newpath = apr_pstrmemdup(p, path, pathlen);
+ return APR_SUCCESS;
+}
+
+
+APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
+ const char *liststr,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
+}
+
+APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
+ apr_array_header_t *pathelts,
+ apr_pool_t *p)
+{
+ return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
+}
+
+
+APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
+{
+#if APR_HAS_UNICODE_FS
+ IF_WIN_OS_IS_UNICODE
+ {
+ *style = APR_FILEPATH_ENCODING_UTF8;
+ return APR_SUCCESS;
+ }
+#endif
+
+ *style = APR_FILEPATH_ENCODING_LOCALE;
+ return APR_SUCCESS;
+}
diff --git a/file_io/win32/filestat.c b/file_io/win32/filestat.c
new file mode 100644
index 0000000..5ddee7f
--- /dev/null
+++ b/file_io/win32/filestat.c
@@ -0,0 +1,899 @@
+/* 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);
+}
+
+static int reparse_point_is_link(WIN32_FILE_ATTRIBUTE_DATA *wininfo,
+ int finddata, const char *fname)
+{
+ int tag = 0;
+
+ if (!(wininfo->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ return 0;
+ }
+
+ if (finddata)
+ {
+ // no matter A or W as we don't need file name
+ tag = ((WIN32_FIND_DATAA*)wininfo)->dwReserved0;
+ }
+ else
+ {
+ if (test_safe_name(fname) != APR_SUCCESS) {
+ return 0;
+ }
+
+#if APR_HAS_UNICODE_FS
+ IF_WIN_OS_IS_UNICODE
+ {
+ apr_wchar_t wfname[APR_PATH_MAX];
+ HANDLE hFind;
+ WIN32_FIND_DATAW fd;
+
+ if (utf8_to_unicode_path(wfname, APR_PATH_MAX, fname) != APR_SUCCESS) {
+ return 0;
+ }
+
+ hFind = FindFirstFileW(wfname, &fd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ FindClose(hFind);
+
+ tag = fd.dwReserved0;
+ }
+#endif
+#if APR_HAS_ANSI_FS || 1
+ ELSE_WIN_OS_IS_ANSI
+ {
+ HANDLE hFind;
+ WIN32_FIND_DATAA fd;
+
+ hFind = FindFirstFileA(fname, &fd);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ FindClose(hFind);
+
+ tag = fd.dwReserved0;
+ }
+#endif
+ }
+
+ // Test "Name surrogate bit" to detect any kind of symbolic link
+ // See https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
+ return tag & 0x20000000;
+}
+
+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,
+ int finddata,
+ const char *fname,
+ 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 &&
+ reparse_point_is_link(wininfo, finddata, fname)) {
+ 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, 0, thefile->fname, 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;
+ int finddata = 0;
+
+ /* 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);
+ finddata = 1;
+
+ 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 | APR_FINFO_LINK)) || 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);
+ finddata = 1;
+ if (wanted & APR_FINFO_NAME) {
+ filename = apr_pstrdup(pool, FileInfo.n.cFileName);
+ }
+ }
+ }
+#endif
+
+ if (ident_rv != APR_INCOMPLETE) {
+ if (fillin_fileinfo(finfo, (WIN32_FILE_ATTRIBUTE_DATA *) &FileInfo,
+ 0, finddata, fname, 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;
+}
diff --git a/file_io/win32/filesys.c b/file_io/win32/filesys.c
new file mode 100644
index 0000000..e812139
--- /dev/null
+++ b/file_io/win32/filesys.c
@@ -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;
+}
diff --git a/file_io/win32/flock.c b/file_io/win32/flock.c
new file mode 100644
index 0000000..e08e08a
--- /dev/null
+++ b/file_io/win32/flock.c
@@ -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) */
+}
diff --git a/file_io/win32/open.c b/file_io/win32/open.c
new file mode 100644
index 0000000..3c00bfc
--- /dev/null
+++ b/file_io/win32/open.c
@@ -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)
diff --git a/file_io/win32/pipe.c b/file_io/win32/pipe.c
new file mode 100644
index 0000000..dd8ef13
--- /dev/null
+++ b/file_io/win32/pipe.c
@@ -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;
+}
+
diff --git a/file_io/win32/readwrite.c b/file_io/win32/readwrite.c
new file mode 100644
index 0000000..701bec7
--- /dev/null
+++ b/file_io/win32/readwrite.c
@@ -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;
+}
diff --git a/file_io/win32/seek.c b/file_io/win32/seek.c
new file mode 100644
index 0000000..dfef577
--- /dev/null
+++ b/file_io/win32/seek.c
@@ -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 = (apr_size_t)(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;
+}