From 30d479c28c831a0d4f1fdb54a9e346b0fc176be1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 08:23:09 +0200 Subject: Adding upstream version 1.7.2. Signed-off-by: Daniel Baumann --- file_io/unix/buffer.c | 60 +++++ file_io/unix/copy.c | 119 ++++++++++ file_io/unix/dir.c | 373 ++++++++++++++++++++++++++++++ file_io/unix/fileacc.c | 119 ++++++++++ file_io/unix/filedup.c | 181 +++++++++++++++ file_io/unix/filepath.c | 314 +++++++++++++++++++++++++ file_io/unix/filepath_util.c | 111 +++++++++ file_io/unix/filestat.c | 339 +++++++++++++++++++++++++++ file_io/unix/flock.c | 120 ++++++++++ file_io/unix/fullrw.c | 111 +++++++++ file_io/unix/mktemp.c | 226 ++++++++++++++++++ file_io/unix/open.c | 417 +++++++++++++++++++++++++++++++++ file_io/unix/pipe.c | 292 +++++++++++++++++++++++ file_io/unix/readwrite.c | 538 +++++++++++++++++++++++++++++++++++++++++++ file_io/unix/seek.c | 136 +++++++++++ file_io/unix/tempdir.c | 129 +++++++++++ 16 files changed, 3585 insertions(+) create mode 100644 file_io/unix/buffer.c create mode 100644 file_io/unix/copy.c create mode 100644 file_io/unix/dir.c create mode 100644 file_io/unix/fileacc.c create mode 100644 file_io/unix/filedup.c create mode 100644 file_io/unix/filepath.c create mode 100644 file_io/unix/filepath_util.c create mode 100644 file_io/unix/filestat.c create mode 100644 file_io/unix/flock.c create mode 100644 file_io/unix/fullrw.c create mode 100644 file_io/unix/mktemp.c create mode 100644 file_io/unix/open.c create mode 100644 file_io/unix/pipe.c create mode 100644 file_io/unix/readwrite.c create mode 100644 file_io/unix/seek.c create mode 100644 file_io/unix/tempdir.c (limited to 'file_io/unix') 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 +#endif +#if APR_HAVE_LIMITS_H +#include +#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 +#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 +#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 +#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 +#endif +#ifdef HAVE_SYS_FILE_H +#include +#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 +#endif +#endif +#define arc4random() rand() +#define seedrandom(a) srand(a) +#else +#if APR_HAVE_STDINT_H +#include +#endif +#define arc4random() random() +#define seedrandom(a) srandom(a) +#endif + +#if APR_HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#if APR_HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_TIME_H +#include +#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 /* for mkstemp() - Single Unix */ +#endif +#if APR_HAVE_UNISTD_H +#include /* 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; +} -- cgit v1.2.3