/*
Authors:
Jakub Hrozek <jhrozek@redhat.com>
Copyright (C) 2009 Red Hat
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 1991 - 1994, Julianne Frances Haugh
* Copyright (c) 1996 - 2001, Marek Michałkiewicz
* Copyright (c) 2003 - 2006, Tomasz Kłoczko
* Copyright (c) 2007 - 2008, Nicolas François
*
* 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
* 3. The name of the copyright holders or contributors may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
* HOLDERS 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 "config.h"
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <talloc.h>
#include "tools/tools_util.h"
struct copy_ctx {
const char *src_orig;
const char *dst_orig;
};
int flags)
{
int ret;
#ifdef HAVE_UTIMENSAT
#else
#endif
if (ret == -1) {
return errno;
}
return EOK;
}
{
int ret;
#ifdef HAVE_FUTIMENS
#else
#endif
if (ret == -1) {
return errno;
}
return EOK;
}
/* wrapper in order not to create a temporary context in
* every iteration */
int parent_fd,
const char *dir_name,
bool keep_root_dir);
{
int ret;
if (!tmp_ctx) {
return ENOMEM;
}
return ret;
}
{
int ret;
if (!tmp_ctx) {
return ENOMEM;
}
return ret;
}
/*
* The context is not freed in case of error
* because this is a recursive function, will be freed when we
* reach the top level remove_tree() again
*/
int parent_fd,
const char *dir_name,
bool keep_root_dir)
{
int dir_fd;
if (dir_fd == -1) {
return ret;
}
goto fail;
}
continue;
}
if (ret != 0) {
goto fail;
}
/* if directory, recursively descend, but check if on the same FS */
"Directory %s is on different filesystem, "
goto fail;
}
"Removing subdirectory failed: [%d][%s]\n",
goto fail;
}
} else {
if (ret != 0) {
"Removing file failed '%s': [%d][%s]\n",
goto fail;
}
}
}
if (ret != 0) {
goto fail;
}
if (!keep_root_dir) {
/* Remove also root directory. */
if (ret == -1) {
}
}
fail:
if (rootdir) { /* clean up on abnormal exit but retain return code */
if (err) {
}
}
return ret;
}
const char *filename)
{
char *buffer;
char *new_buffer;
if (!buffer) {
return NULL;
}
while (1) {
if (nchars < 0) {
return NULL;
}
/* The buffer was large enough */
break;
}
/* Try again with a bigger buffer */
size *= 2;
if (!new_buffer) {
return NULL;
}
buffer = new_buffer;
}
/* readlink does not nul-terminate */
return buffer;
}
static int
int dst_dir_fd,
const char *file_name,
const char *full_path,
{
char *buf;
if (!buf) {
return ENOMEM;
}
if (ret != 0) {
"Failed to set SELinux context for [%s]\n", full_path);
/* Not fatal */
}
if (ret == -1) {
"symlink pointing to already exists at '%s'\n", full_path);
return EOK;
}
return ret;
}
if (ret == -1) {
return ret;
}
/* Do not fail */
}
return EOK;
}
static int
int ofd,
{
if (cnt == -1) {
"Cannot read() from source file: [%d][%s].\n",
goto done;
}
errno = 0;
if (written == -1) {
"Cannot write() to destination file: [%d][%s].\n",
goto done;
}
goto done;
}
}
/* Set the ownership; permissions are still
* restrictive. */
"Error changing owner: %s\n",
goto done;
}
/* Set the desired mode. */
if (ret == -1) {
goto done;
}
done:
return ret;
}
/* Copy bytes from input file descriptor ifd into file named
* dst_named under directory with dest_dir_fd. Own the new file
*/
static int
int dest_dir_fd,
const char *file_name,
const char *full_path,
{
if (ret != 0) {
"Failed to set SELinux context for [%s]\n", full_path);
/* Not fatal */
}
/* Start with absolutely restrictive permissions */
0);
"Cannot open() destination file '%s': [%d][%s].\n",
goto done;
}
/* Do not fail */
}
done:
return ret;
}
int
const char *dest,
bool force)
{
int dest_flags = 0;
if (ret != 0) {
"Failed to set SELinux context for [%s]\n", dest);
/* Not fatal */
}
/* Start with absolutely restrictive permissions */
if (!force) {
dest_flags |= O_EXCL;
}
if (ofd < 0) {
"Cannot open() destination file '%s': [%d][%s].\n",
goto done;
}
if (ifd < 0) {
"Cannot open() source file '%s': [%d][%s].\n",
goto done;
}
done:
return ret;
}
static errno_t
int src_dir_fd, const char *src_dir_path,
int dest_parent_fd, const char *dest_dir_name,
const char *dest_dir_path,
const struct stat *src_dir_stat);
static errno_t
int src_dir_fd,
const char *src_dir_path,
int dest_dir_fd,
const char *dest_dir_path,
const char *ent_name)
{
/* Build the path of the source file or directory and its
* corresponding member in the new tree. */
if (!src_ent_path || !dest_ent_path) {
goto done;
}
/* Open the input entry first, then we can fstat() it and be
* certain that it is still the same file. O_NONBLOCK protects
* us against FIFOs and perhaps side-effects of the open() of a
* device file if there ever was one here, and doesn't matter
* for regular files or directories. */
/* openat error */
goto done;
/* Should be a symlink.. */
if (ret == -1) {
goto done;
}
/* Handle symlinks */
}
goto done;
}
if (ret != 0) {
goto done;
}
/* If it's a directory, descend into it. */
&st);
"Couldn't recursively copy '%s' to '%s': %s\n",
goto done;
}
/* Copy a regular file */
if (ret) {
goto done;
}
} else {
/* Is a special file */
}
done:
return ret;
}
static errno_t
int src_dir_fd, const char *src_dir_path,
int dest_parent_fd, const char *dest_dir_name,
const char *dest_dir_path,
const struct stat *src_dir_stat)
{
if (!dest_dir_path) {
return EINVAL;
}
goto done;
}
/* Create the directory. It starts owned by us (presumbaly root), with
* fairly restrictive permissions that still allow us to use the
* directory.
* */
errno = 0;
goto done;
}
if (dest_dir_fd == -1) {
goto done;
}
/* Iterate through each item in the directory. */
/* Skip over self and parent hard links. */
continue;
}
goto done;
}
}
/* Set the ownership on the directory. Permissions are still
* fairly restrictive. */
"Error changing owner of '%s': %s\n",
goto done;
}
/* Set the desired mode. Do this explicitly to preserve S_ISGID and
* other bits. Do this after chown, because chown is permitted to
* reset these bits. */
if (ret == -1) {
"Error setting mode of '%s': %s\n",
goto done;
}
/* Do not fail */
}
done:
if (dir) {
if (dret != 0) {
}
}
if (dest_dir_fd != -1) {
}
return ret;
}
/* NOTE:
* For several reasons, including the fact that we copy even special files
* (pipes, etc) from the skeleton directory, the skeldir needs to be trusted
*/
{
if (fd == -1) {
goto fail;
}
if (ret == -1) {
goto fail;
}
if (!cctx) {
goto fail;
}
goto fail;
}
fail:
return ret;
}