diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:23:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:23:09 +0000 |
commit | 30d479c28c831a0d4f1fdb54a9e346b0fc176be1 (patch) | |
tree | aa35d7414ce9f1326abf6f723f6dfa5b0aa08b1d /file_io | |
parent | Initial commit. (diff) | |
download | apr-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')
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; +} |