Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 238aa92948 | |||
| faeab50e71 | |||
| 6cbec2d0aa | |||
| f606314f0c | |||
| 1d281273b1 | |||
| dab764d019 | |||
| f6f8bcd2a5 | |||
| e9ae247cb1 |
@@ -1,3 +1,8 @@
|
|||||||
|
2022-08-15 Serge Hallyn <serge@hallyn.com>
|
||||||
|
|
||||||
|
* Address CVE-2013-4235 (TOCTTOU when copying directories)
|
||||||
|
(Christian Göttsche)
|
||||||
|
|
||||||
2022-08-15 Serge Hallyn <serge@hallyn.com>
|
2022-08-15 Serge Hallyn <serge@hallyn.com>
|
||||||
|
|
||||||
* Fix uk manpages
|
* Fix uk manpages
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ m4_define([libsubid_abi_major], 4)
|
|||||||
m4_define([libsubid_abi_minor], 0)
|
m4_define([libsubid_abi_minor], 0)
|
||||||
m4_define([libsubid_abi_micro], 0)
|
m4_define([libsubid_abi_micro], 0)
|
||||||
m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
|
m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
|
||||||
AC_INIT([shadow], [4.12.1], [pkg-shadow-devel@lists.alioth.debian.org], [],
|
AC_INIT([shadow], [4.12.2], [pkg-shadow-devel@lists.alioth.debian.org], [],
|
||||||
[https://github.com/shadow-maint/shadow])
|
[https://github.com/shadow-maint/shadow])
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
|
AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
|
||||||
AC_CONFIG_MACRO_DIRS([m4])
|
AC_CONFIG_MACRO_DIRS([m4])
|
||||||
@@ -49,7 +49,7 @@ AC_CHECK_HEADER([shadow.h],,[AC_MSG_ERROR([You need a libc with shadow.h])])
|
|||||||
|
|
||||||
AC_CHECK_FUNCS(arc4random_buf l64a fchmod fchown fsync futimes \
|
AC_CHECK_FUNCS(arc4random_buf l64a fchmod fchown fsync futimes \
|
||||||
getentropy getrandom getspnam getusershell \
|
getentropy getrandom getspnam getusershell \
|
||||||
getutent initgroups lchown lckpwdf lstat lutimes \
|
getutent initgroups lckpwdf lutimes \
|
||||||
setgroups updwtmp updwtmpx innetgr getpwnam_r \
|
setgroups updwtmp updwtmpx innetgr getpwnam_r \
|
||||||
getpwuid_r getgrnam_r getgrgid_r getspnam_r \
|
getpwuid_r getgrnam_r getgrgid_r getspnam_r \
|
||||||
memset_s explicit_bzero)
|
memset_s explicit_bzero)
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ int lrename (const char *old, const char *new)
|
|||||||
int res;
|
int res;
|
||||||
char *r = NULL;
|
char *r = NULL;
|
||||||
|
|
||||||
#if defined(S_ISLNK)
|
|
||||||
#ifndef __GLIBC__
|
#ifndef __GLIBC__
|
||||||
char resolved_path[PATH_MAX];
|
char resolved_path[PATH_MAX];
|
||||||
#endif /* !__GLIBC__ */
|
#endif /* !__GLIBC__ */
|
||||||
@@ -82,7 +81,6 @@ int lrename (const char *old, const char *new)
|
|||||||
new = r;
|
new = r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* S_ISLNK */
|
|
||||||
|
|
||||||
res = rename (old, new);
|
res = rename (old, new);
|
||||||
|
|
||||||
|
|||||||
@@ -205,22 +205,6 @@ static inline void memzero(void *ptr, size_t size)
|
|||||||
# define SEEK_END 2
|
# define SEEK_END 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef S_ISLNK
|
|
||||||
#define S_ISLNK(x) (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_LCHOWN
|
|
||||||
#define LCHOWN lchown
|
|
||||||
#else
|
|
||||||
#define LCHOWN chown
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_LSTAT
|
|
||||||
#define LSTAT lstat
|
|
||||||
#else
|
|
||||||
#define LSTAT stat
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_TERMIOS_H
|
#if HAVE_TERMIOS_H
|
||||||
# include <termios.h>
|
# include <termios.h>
|
||||||
# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
|
# define STTY(fd, termio) tcsetattr(fd, TCSANOW, termio)
|
||||||
|
|||||||
+107
-139
@@ -17,6 +17,112 @@
|
|||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int chown_tree_at (int at_fd,
|
||||||
|
const char *path,
|
||||||
|
uid_t old_uid,
|
||||||
|
uid_t new_uid,
|
||||||
|
gid_t old_gid,
|
||||||
|
gid_t new_gid)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
const struct dirent *ent;
|
||||||
|
struct stat dir_sb;
|
||||||
|
int dir_fd, rc = 0;
|
||||||
|
|
||||||
|
dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (dir_fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = fdopendir (dir_fd);
|
||||||
|
if (!dir) {
|
||||||
|
(void) close (dir_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the directory and read each entry. Every entry is tested
|
||||||
|
* to see if it is a directory, and if so this routine is called
|
||||||
|
* recursively. If not, it is checked to see if an ownership
|
||||||
|
* shall be changed.
|
||||||
|
*/
|
||||||
|
while ((ent = readdir (dir))) {
|
||||||
|
uid_t tmpuid = (uid_t) -1;
|
||||||
|
gid_t tmpgid = (gid_t) -1;
|
||||||
|
struct stat ent_sb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip the "." and ".." entries
|
||||||
|
*/
|
||||||
|
if ( (strcmp (ent->d_name, ".") == 0)
|
||||||
|
|| (strcmp (ent->d_name, "..") == 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW);
|
||||||
|
if (rc < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR (ent_sb.st_mode)) {
|
||||||
|
/*
|
||||||
|
* Do the entire subdirectory.
|
||||||
|
*/
|
||||||
|
rc = chown_tree_at (dirfd(dir), ent->d_name, old_uid, new_uid, old_gid, new_gid);
|
||||||
|
if (0 != rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default, the IDs are not changed (-1).
|
||||||
|
*
|
||||||
|
* If the file is not owned by the user, the owner is not
|
||||||
|
* changed.
|
||||||
|
*
|
||||||
|
* If the file is not group-owned by the group, the
|
||||||
|
* group-owner is not changed.
|
||||||
|
*/
|
||||||
|
if (((uid_t) -1 == old_uid) || (ent_sb.st_uid == old_uid)) {
|
||||||
|
tmpuid = new_uid;
|
||||||
|
}
|
||||||
|
if (((gid_t) -1 == old_gid) || (ent_sb.st_gid == old_gid)) {
|
||||||
|
tmpgid = new_gid;
|
||||||
|
}
|
||||||
|
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
|
||||||
|
rc = fchownat (dirfd(dir), ent->d_name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW);
|
||||||
|
if (0 != rc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now do the root of the tree
|
||||||
|
*/
|
||||||
|
if ((0 == rc) && (fstat (dirfd(dir), &dir_sb) == 0)) {
|
||||||
|
uid_t tmpuid = (uid_t) -1;
|
||||||
|
gid_t tmpgid = (gid_t) -1;
|
||||||
|
if (((uid_t) -1 == old_uid) || (dir_sb.st_uid == old_uid)) {
|
||||||
|
tmpuid = new_uid;
|
||||||
|
}
|
||||||
|
if (((gid_t) -1 == old_gid) || (dir_sb.st_gid == old_gid)) {
|
||||||
|
tmpgid = new_gid;
|
||||||
|
}
|
||||||
|
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
|
||||||
|
rc = fchown (dirfd(dir), tmpuid, tmpgid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) closedir (dir);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* chown_tree - change ownership of files in a directory tree
|
* chown_tree - change ownership of files in a directory tree
|
||||||
*
|
*
|
||||||
@@ -36,143 +142,5 @@ int chown_tree (const char *root,
|
|||||||
gid_t old_gid,
|
gid_t old_gid,
|
||||||
gid_t new_gid)
|
gid_t new_gid)
|
||||||
{
|
{
|
||||||
char *new_name;
|
return chown_tree_at (AT_FDCWD, root, old_uid, new_uid, old_gid, new_gid);
|
||||||
size_t new_name_len;
|
|
||||||
int rc = 0;
|
|
||||||
struct dirent *ent;
|
|
||||||
struct stat sb;
|
|
||||||
DIR *dir;
|
|
||||||
|
|
||||||
new_name = malloc (1024);
|
|
||||||
if (NULL == new_name) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
new_name_len = 1024;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make certain the directory exists. This routine is called
|
|
||||||
* directly by the invoker, or recursively.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (access (root, F_OK) != 0) {
|
|
||||||
free (new_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open the directory and read each entry. Every entry is tested
|
|
||||||
* to see if it is a directory, and if so this routine is called
|
|
||||||
* recursively. If not, it is checked to see if an ownership
|
|
||||||
* shall be changed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
dir = opendir (root);
|
|
||||||
if (NULL == dir) {
|
|
||||||
free (new_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = readdir (dir))) {
|
|
||||||
size_t ent_name_len;
|
|
||||||
uid_t tmpuid = (uid_t) -1;
|
|
||||||
gid_t tmpgid = (gid_t) -1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip the "." and ".." entries
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ( (strcmp (ent->d_name, ".") == 0)
|
|
||||||
|| (strcmp (ent->d_name, "..") == 0)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make the filename for both the source and the
|
|
||||||
* destination files.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ent_name_len = strlen (root) + strlen (ent->d_name) + 2;
|
|
||||||
if (ent_name_len > new_name_len) {
|
|
||||||
/*@only@*/char *tmp = realloc (new_name, ent_name_len);
|
|
||||||
if (NULL == tmp) {
|
|
||||||
rc = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
new_name = tmp;
|
|
||||||
new_name_len = ent_name_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) snprintf (new_name, new_name_len, "%s/%s", root, ent->d_name);
|
|
||||||
|
|
||||||
/* Don't follow symbolic links! */
|
|
||||||
if (LSTAT (new_name, &sb) == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (S_ISDIR (sb.st_mode) && !S_ISLNK (sb.st_mode)) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do the entire subdirectory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
rc = chown_tree (new_name, old_uid, new_uid,
|
|
||||||
old_gid, new_gid);
|
|
||||||
if (0 != rc) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifndef HAVE_LCHOWN
|
|
||||||
/* don't use chown (follows symbolic links!) */
|
|
||||||
if (S_ISLNK (sb.st_mode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
* By default, the IDs are not changed (-1).
|
|
||||||
*
|
|
||||||
* If the file is not owned by the user, the owner is not
|
|
||||||
* changed.
|
|
||||||
*
|
|
||||||
* If the file is not group-owned by the group, the
|
|
||||||
* group-owner is not changed.
|
|
||||||
*/
|
|
||||||
if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) {
|
|
||||||
tmpuid = new_uid;
|
|
||||||
}
|
|
||||||
if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) {
|
|
||||||
tmpgid = new_gid;
|
|
||||||
}
|
|
||||||
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
|
|
||||||
rc = LCHOWN (new_name, tmpuid, tmpgid);
|
|
||||||
if (0 != rc) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free (new_name);
|
|
||||||
(void) closedir (dir);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now do the root of the tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((0 == rc) && (stat (root, &sb) == 0)) {
|
|
||||||
uid_t tmpuid = (uid_t) -1;
|
|
||||||
gid_t tmpgid = (gid_t) -1;
|
|
||||||
if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) {
|
|
||||||
tmpuid = new_uid;
|
|
||||||
}
|
|
||||||
if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) {
|
|
||||||
tmpgid = new_gid;
|
|
||||||
}
|
|
||||||
if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) {
|
|
||||||
rc = LCHOWN (root, tmpuid, tmpgid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rc = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+275
-125
@@ -47,42 +47,43 @@ struct link_name {
|
|||||||
};
|
};
|
||||||
static /*@exposed@*/struct link_name *links;
|
static /*@exposed@*/struct link_name *links;
|
||||||
|
|
||||||
static int copy_entry (const char *src, const char *dst,
|
struct path_info {
|
||||||
|
const char *full_path;
|
||||||
|
int dirfd;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int copy_entry (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
static int copy_dir (const char *src, const char *dst,
|
static int copy_dir (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
#ifdef S_IFLNK
|
|
||||||
static /*@null@*/char *readlink_malloc (const char *filename);
|
static /*@null@*/char *readlink_malloc (const char *filename);
|
||||||
static int copy_symlink (const char *src, const char *dst,
|
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
|
||||||
unused bool reset_selinux,
|
unused bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
#endif /* S_IFLNK */
|
static int copy_hardlink (const struct path_info *dst,
|
||||||
static int copy_hardlink (const char *dst,
|
|
||||||
unused bool reset_selinux,
|
unused bool reset_selinux,
|
||||||
struct link_name *lp);
|
struct link_name *lp);
|
||||||
static int copy_special (const char *src, const char *dst,
|
static int copy_special (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
static int copy_file (const char *src, const char *dst,
|
static int copy_file (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
static int chown_if_needed (const char *dst, const struct stat *statp,
|
static int chownat_if_needed (const struct path_info *dst, const struct stat *statp,
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
static int lchown_if_needed (const char *dst, const struct stat *statp,
|
|
||||||
uid_t old_uid, uid_t new_uid,
|
|
||||||
gid_t old_gid, gid_t new_gid);
|
|
||||||
static int fchown_if_needed (int fdst, const struct stat *statp,
|
static int fchown_if_needed (int fdst, const struct stat *statp,
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid);
|
gid_t old_gid, gid_t new_gid);
|
||||||
@@ -114,10 +115,61 @@ static void error_acl (unused struct error_context *ctx, const char *fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct error_context ctx = {
|
static struct error_context ctx = {
|
||||||
error_acl
|
error_acl, NULL, NULL
|
||||||
};
|
};
|
||||||
#endif /* WITH_ACL || WITH_ATTR */
|
#endif /* WITH_ACL || WITH_ATTR */
|
||||||
|
|
||||||
|
#ifdef WITH_ACL
|
||||||
|
static int perm_copy_path(const struct path_info *src,
|
||||||
|
const struct path_info *dst,
|
||||||
|
struct error_context *errctx)
|
||||||
|
{
|
||||||
|
int src_fd, dst_fd, ret;
|
||||||
|
|
||||||
|
src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (src_fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (dst_fd < 0) {
|
||||||
|
(void) close (src_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = perm_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, errctx);
|
||||||
|
(void) close (src_fd);
|
||||||
|
(void) close (dst_fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* WITH_ACL */
|
||||||
|
|
||||||
|
#ifdef WITH_ATTR
|
||||||
|
static int attr_copy_path(const struct path_info *src,
|
||||||
|
const struct path_info *dst,
|
||||||
|
int (*callback) (const char *, struct error_context *),
|
||||||
|
struct error_context *errctx)
|
||||||
|
{
|
||||||
|
int src_fd, dst_fd, ret;
|
||||||
|
|
||||||
|
src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (src_fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (dst_fd < 0) {
|
||||||
|
(void) close (src_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = attr_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, callback, errctx);
|
||||||
|
(void) close (src_fd);
|
||||||
|
(void) close (dst_fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* WITH_ATTR */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* remove_link - delete a link from the linked list
|
* remove_link - delete a link from the linked list
|
||||||
*/
|
*/
|
||||||
@@ -190,51 +242,36 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int copy_tree_impl (const struct path_info *src, const struct path_info *dst,
|
||||||
* copy_tree - copy files in a directory tree
|
|
||||||
*
|
|
||||||
* copy_tree() walks a directory tree and copies ordinary files
|
|
||||||
* as it goes.
|
|
||||||
*
|
|
||||||
* When reset_selinux is enabled, extended attributes (and thus
|
|
||||||
* SELinux attributes) are not copied.
|
|
||||||
*
|
|
||||||
* old_uid and new_uid are used to set the ownership of the copied
|
|
||||||
* files. Unless old_uid is set to -1, only the files owned by
|
|
||||||
* old_uid have their ownership changed to new_uid. In addition, if
|
|
||||||
* new_uid is set to -1, no ownership will be changed.
|
|
||||||
*
|
|
||||||
* The same logic applies for the group-ownership and
|
|
||||||
* old_gid/new_gid.
|
|
||||||
*/
|
|
||||||
int copy_tree (const char *src_root, const char *dst_root,
|
|
||||||
bool copy_root, bool reset_selinux,
|
bool copy_root, bool reset_selinux,
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int dst_fd, src_fd, err = 0;
|
||||||
bool set_orig = false;
|
bool set_orig = false;
|
||||||
struct dirent *ent;
|
const struct dirent *ent;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
|
||||||
if (copy_root) {
|
if (copy_root) {
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
if (access (dst_root, F_OK) == 0) {
|
|
||||||
|
if ( fstatat (dst->dirfd, dst->name, &sb, 0) == 0
|
||||||
|
|| errno != ENOENT) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LSTAT (src_root, &sb) == -1) {
|
if (fstatat (src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISDIR (sb.st_mode)) {
|
if (!S_ISDIR (sb.st_mode)) {
|
||||||
fprintf (log_get_logfd(),
|
fprintf (log_get_logfd(),
|
||||||
"%s: %s is not a directory",
|
"%s: %s is not a directory",
|
||||||
log_get_progname(), src_root);
|
log_get_progname(), src->full_path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return copy_entry (src_root, dst_root, reset_selinux,
|
return copy_entry (src, dst, reset_selinux,
|
||||||
old_uid, new_uid, old_gid, new_gid);
|
old_uid, new_uid, old_gid, new_gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +281,14 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
* target is created. It assumes the target directory exists.
|
* target is created. It assumes the target directory exists.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ( (access (src_root, F_OK) != 0)
|
src_fd = openat (src->dirfd, src->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|| (access (dst_root, F_OK) != 0)) {
|
if (src_fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_fd = openat (dst->dirfd, dst->name, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (dst_fd < 0) {
|
||||||
|
(void) close (src_fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,14 +299,16 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
* regular files (and directories ...) are copied, and no file
|
* regular files (and directories ...) are copied, and no file
|
||||||
* is made set-ID.
|
* is made set-ID.
|
||||||
*/
|
*/
|
||||||
dir = opendir (src_root);
|
dir = fdopendir (src_fd);
|
||||||
if (NULL == dir) {
|
if (NULL == dir) {
|
||||||
|
(void) close (src_fd);
|
||||||
|
(void) close (dst_fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_orig == NULL) {
|
if (src_orig == NULL) {
|
||||||
src_orig = src_root;
|
src_orig = src->full_path;
|
||||||
dst_orig = dst_root;
|
dst_orig = dst->full_path;
|
||||||
set_orig = true;
|
set_orig = true;
|
||||||
}
|
}
|
||||||
while ((0 == err) && (ent = readdir (dir)) != NULL) {
|
while ((0 == err) && (ent = readdir (dir)) != NULL) {
|
||||||
@@ -276,8 +321,8 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
char *dst_name;
|
char *dst_name;
|
||||||
size_t src_len = strlen (ent->d_name) + 2;
|
size_t src_len = strlen (ent->d_name) + 2;
|
||||||
size_t dst_len = strlen (ent->d_name) + 2;
|
size_t dst_len = strlen (ent->d_name) + 2;
|
||||||
src_len += strlen (src_root);
|
src_len += strlen (src->full_path);
|
||||||
dst_len += strlen (dst_root);
|
dst_len += strlen (dst->full_path);
|
||||||
|
|
||||||
src_name = (char *) malloc (src_len);
|
src_name = (char *) malloc (src_len);
|
||||||
dst_name = (char *) malloc (dst_len);
|
dst_name = (char *) malloc (dst_len);
|
||||||
@@ -289,12 +334,22 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
* Build the filename for both the source and
|
* Build the filename for both the source and
|
||||||
* the destination files.
|
* the destination files.
|
||||||
*/
|
*/
|
||||||
(void) snprintf (src_name, src_len, "%s/%s",
|
struct path_info src_entry, dst_entry;
|
||||||
src_root, ent->d_name);
|
|
||||||
(void) snprintf (dst_name, dst_len, "%s/%s",
|
|
||||||
dst_root, ent->d_name);
|
|
||||||
|
|
||||||
err = copy_entry (src_name, dst_name,
|
(void) snprintf (src_name, src_len, "%s/%s",
|
||||||
|
src->full_path, ent->d_name);
|
||||||
|
(void) snprintf (dst_name, dst_len, "%s/%s",
|
||||||
|
dst->full_path, ent->d_name);
|
||||||
|
|
||||||
|
src_entry.full_path = src_name;
|
||||||
|
src_entry.dirfd = dirfd(dir);
|
||||||
|
src_entry.name = ent->d_name;
|
||||||
|
|
||||||
|
dst_entry.full_path = dst_name;
|
||||||
|
dst_entry.dirfd = dst_fd;
|
||||||
|
dst_entry.name = ent->d_name;
|
||||||
|
|
||||||
|
err = copy_entry (&src_entry, &dst_entry,
|
||||||
reset_selinux,
|
reset_selinux,
|
||||||
old_uid, new_uid,
|
old_uid, new_uid,
|
||||||
old_gid, new_gid);
|
old_gid, new_gid);
|
||||||
@@ -308,6 +363,7 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) closedir (dir);
|
(void) closedir (dir);
|
||||||
|
(void) close (dst_fd);
|
||||||
|
|
||||||
if (set_orig) {
|
if (set_orig) {
|
||||||
src_orig = NULL;
|
src_orig = NULL;
|
||||||
@@ -354,7 +410,7 @@ int copy_tree (const char *src_root, const char *dst_root,
|
|||||||
* old_gid) will be modified, unless old_uid (resp. old_gid) is set
|
* old_gid) will be modified, unless old_uid (resp. old_gid) is set
|
||||||
* to -1.
|
* to -1.
|
||||||
*/
|
*/
|
||||||
static int copy_entry (const char *src, const char *dst,
|
static int copy_entry (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
@@ -362,32 +418,32 @@ static int copy_entry (const char *src, const char *dst,
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct link_name *lp;
|
struct link_name *lp;
|
||||||
struct timeval mt[2];
|
struct timespec mt[2];
|
||||||
|
|
||||||
if (LSTAT (src, &sb) == -1) {
|
if (fstatat(src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||||
/* If we cannot stat the file, do not care. */
|
/* If we cannot stat the file, do not care. */
|
||||||
} else {
|
} else {
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||||
mt[0].tv_sec = sb.st_atim.tv_sec;
|
mt[0].tv_sec = sb.st_atim.tv_sec;
|
||||||
mt[0].tv_usec = sb.st_atim.tv_nsec / 1000;
|
mt[0].tv_nsec = sb.st_atim.tv_nsec;
|
||||||
#else /* !HAVE_STRUCT_STAT_ST_ATIM */
|
#else /* !HAVE_STRUCT_STAT_ST_ATIM */
|
||||||
mt[0].tv_sec = sb.st_atime;
|
mt[0].tv_sec = sb.st_atime;
|
||||||
# ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
|
# ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
|
||||||
mt[0].tv_usec = sb.st_atimensec / 1000;
|
mt[0].tv_nsec = sb.st_atimensec;
|
||||||
# else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
|
# else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
|
||||||
mt[0].tv_usec = 0;
|
mt[0].tv_nsec = 0;
|
||||||
# endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
|
# endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
|
||||||
#endif /* !HAVE_STRUCT_STAT_ST_ATIM */
|
#endif /* !HAVE_STRUCT_STAT_ST_ATIM */
|
||||||
|
|
||||||
#ifdef HAVE_STRUCT_STAT_ST_MTIM
|
#ifdef HAVE_STRUCT_STAT_ST_MTIM
|
||||||
mt[1].tv_sec = sb.st_mtim.tv_sec;
|
mt[1].tv_sec = sb.st_mtim.tv_sec;
|
||||||
mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000;
|
mt[1].tv_nsec = sb.st_mtim.tv_nsec;
|
||||||
#else /* !HAVE_STRUCT_STAT_ST_MTIM */
|
#else /* !HAVE_STRUCT_STAT_ST_MTIM */
|
||||||
mt[1].tv_sec = sb.st_mtime;
|
mt[1].tv_sec = sb.st_mtime;
|
||||||
# ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
|
# ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
|
||||||
mt[1].tv_usec = sb.st_mtimensec / 1000;
|
mt[1].tv_nsec = sb.st_mtimensec;
|
||||||
# else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
|
# else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
|
||||||
mt[1].tv_usec = 0;
|
mt[1].tv_nsec = 0;
|
||||||
# endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
|
# endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
|
||||||
#endif /* !HAVE_STRUCT_STAT_ST_MTIM */
|
#endif /* !HAVE_STRUCT_STAT_ST_MTIM */
|
||||||
|
|
||||||
@@ -396,7 +452,6 @@ static int copy_entry (const char *src, const char *dst,
|
|||||||
old_uid, new_uid, old_gid, new_gid);
|
old_uid, new_uid, old_gid, new_gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef S_IFLNK
|
|
||||||
/*
|
/*
|
||||||
* Copy any symbolic links
|
* Copy any symbolic links
|
||||||
*/
|
*/
|
||||||
@@ -405,13 +460,12 @@ static int copy_entry (const char *src, const char *dst,
|
|||||||
err = copy_symlink (src, dst, reset_selinux, &sb, mt,
|
err = copy_symlink (src, dst, reset_selinux, &sb, mt,
|
||||||
old_uid, new_uid, old_gid, new_gid);
|
old_uid, new_uid, old_gid, new_gid);
|
||||||
}
|
}
|
||||||
#endif /* S_IFLNK */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See if this is a previously copied link
|
* See if this is a previously copied link
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if ((lp = check_link (src, &sb)) != NULL) {
|
else if ((lp = check_link (src->full_path, &sb)) != NULL) {
|
||||||
err = copy_hardlink (dst, reset_selinux, lp);
|
err = copy_hardlink (dst, reset_selinux, lp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,9 +504,9 @@ static int copy_entry (const char *src, const char *dst,
|
|||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
static int copy_dir (const char *src, const char *dst,
|
static int copy_dir (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
{
|
{
|
||||||
@@ -464,15 +518,15 @@ static int copy_dir (const char *src, const char *dst,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef WITH_SELINUX
|
#ifdef WITH_SELINUX
|
||||||
if (set_selinux_file_context (dst, S_IFDIR) != 0) {
|
if (set_selinux_file_context (dst->full_path, S_IFDIR) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif /* WITH_SELINUX */
|
#endif /* WITH_SELINUX */
|
||||||
if ( (mkdir (dst, statp->st_mode) != 0)
|
if ( (mkdirat (dst->dirfd, dst->name, statp->st_mode) != 0)
|
||||||
|| (chown_if_needed (dst, statp,
|
|| (chownat_if_needed (dst, statp,
|
||||||
old_uid, new_uid, old_gid, new_gid) != 0)
|
old_uid, new_uid, old_gid, new_gid) != 0)
|
||||||
#ifdef WITH_ACL
|
#ifdef WITH_ACL
|
||||||
|| ( (perm_copy_file (src, dst, &ctx) != 0)
|
|| ( (perm_copy_path (src, dst, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#else /* !WITH_ACL */
|
#else /* !WITH_ACL */
|
||||||
|| (chmod (dst, statp->st_mode) != 0)
|
|| (chmod (dst, statp->st_mode) != 0)
|
||||||
@@ -486,19 +540,18 @@ static int copy_dir (const char *src, const char *dst,
|
|||||||
* additional logic so that no unexpected permissions result.
|
* additional logic so that no unexpected permissions result.
|
||||||
*/
|
*/
|
||||||
|| ( !reset_selinux
|
|| ( !reset_selinux
|
||||||
&& (attr_copy_file (src, dst, NULL, &ctx) != 0)
|
&& (attr_copy_path (src, dst, NULL, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#endif /* WITH_ATTR */
|
#endif /* WITH_ATTR */
|
||||||
|| (copy_tree (src, dst, false, reset_selinux,
|
|| (copy_tree_impl (src, dst, false, reset_selinux,
|
||||||
old_uid, new_uid, old_gid, new_gid) != 0)
|
old_uid, new_uid, old_gid, new_gid) != 0)
|
||||||
|| (utimes (dst, mt) != 0)) {
|
|| (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) {
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef S_IFLNK
|
|
||||||
/*
|
/*
|
||||||
* readlink_malloc - wrapper for readlink
|
* readlink_malloc - wrapper for readlink
|
||||||
*
|
*
|
||||||
@@ -545,9 +598,9 @@ static /*@null@*/char *readlink_malloc (const char *filename)
|
|||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
static int copy_symlink (const char *src, const char *dst,
|
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
|
||||||
unused bool reset_selinux,
|
unused bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
{
|
{
|
||||||
@@ -565,7 +618,7 @@ static int copy_symlink (const char *src, const char *dst,
|
|||||||
* destination directory name.
|
* destination directory name.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
oldlink = readlink_malloc (src);
|
oldlink = readlink_malloc (src->full_path);
|
||||||
if (NULL == oldlink) {
|
if (NULL == oldlink) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -585,13 +638,13 @@ static int copy_symlink (const char *src, const char *dst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_SELINUX
|
#ifdef WITH_SELINUX
|
||||||
if (set_selinux_file_context (dst, S_IFLNK) != 0) {
|
if (set_selinux_file_context (dst->full_path, S_IFLNK) != 0) {
|
||||||
free (oldlink);
|
free (oldlink);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif /* WITH_SELINUX */
|
#endif /* WITH_SELINUX */
|
||||||
if ( (symlink (oldlink, dst) != 0)
|
if ( (symlinkat (oldlink, dst->dirfd, dst->name) != 0)
|
||||||
|| (lchown_if_needed (dst, statp,
|
|| (chownat_if_needed (dst, statp,
|
||||||
old_uid, new_uid, old_gid, new_gid) != 0)) {
|
old_uid, new_uid, old_gid, new_gid) != 0)) {
|
||||||
/* FIXME: there are no modes on symlinks, right?
|
/* FIXME: there are no modes on symlinks, right?
|
||||||
* ACL could be copied, but this would be much more
|
* ACL could be copied, but this would be much more
|
||||||
@@ -605,18 +658,12 @@ static int copy_symlink (const char *src, const char *dst,
|
|||||||
}
|
}
|
||||||
free (oldlink);
|
free (oldlink);
|
||||||
|
|
||||||
#ifdef HAVE_LUTIMES
|
if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) {
|
||||||
/* 2007-10-18: We don't care about
|
return -1;
|
||||||
* exit status of lutimes because
|
}
|
||||||
* it returns ENOSYS on many system
|
|
||||||
* - not implemented
|
|
||||||
*/
|
|
||||||
(void) lutimes (dst, mt);
|
|
||||||
#endif /* HAVE_LUTIMES */
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* S_IFLNK */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy_hardlink - copy a hardlink
|
* copy_hardlink - copy a hardlink
|
||||||
@@ -625,13 +672,13 @@ static int copy_symlink (const char *src, const char *dst,
|
|||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
static int copy_hardlink (const char *dst,
|
static int copy_hardlink (const struct path_info *dst,
|
||||||
unused bool reset_selinux,
|
unused bool reset_selinux,
|
||||||
struct link_name *lp)
|
struct link_name *lp)
|
||||||
{
|
{
|
||||||
/* FIXME: selinux, ACL, Extended Attributes needed? */
|
/* FIXME: selinux, ACL, Extended Attributes needed? */
|
||||||
|
|
||||||
if (link (lp->ln_name, dst) != 0) {
|
if (linkat (AT_FDCWD, lp->ln_name, dst->dirfd, dst->name, 0) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,28 +702,28 @@ static int copy_hardlink (const char *dst,
|
|||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
static int copy_special (const char *src, const char *dst,
|
static int copy_special (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
#ifdef WITH_SELINUX
|
#ifdef WITH_SELINUX
|
||||||
if (set_selinux_file_context (dst, statp->st_mode & S_IFMT) != 0) {
|
if (set_selinux_file_context (dst->full_path, statp->st_mode & S_IFMT) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif /* WITH_SELINUX */
|
#endif /* WITH_SELINUX */
|
||||||
|
|
||||||
if ( (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0)
|
if ( (mknodat (dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) != 0)
|
||||||
|| (chown_if_needed (dst, statp,
|
|| (chownat_if_needed (dst, statp,
|
||||||
old_uid, new_uid, old_gid, new_gid) != 0)
|
old_uid, new_uid, old_gid, new_gid) != 0)
|
||||||
#ifdef WITH_ACL
|
#ifdef WITH_ACL
|
||||||
|| ( (perm_copy_file (src, dst, &ctx) != 0)
|
|| ( (perm_copy_path (src, dst, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#else /* !WITH_ACL */
|
#else /* !WITH_ACL */
|
||||||
|| (chmod (dst, statp->st_mode & 07777) != 0)
|
|| (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0)
|
||||||
#endif /* !WITH_ACL */
|
#endif /* !WITH_ACL */
|
||||||
#ifdef WITH_ATTR
|
#ifdef WITH_ATTR
|
||||||
/*
|
/*
|
||||||
@@ -687,16 +734,52 @@ static int copy_special (const char *src, const char *dst,
|
|||||||
* additional logic so that no unexpected permissions result.
|
* additional logic so that no unexpected permissions result.
|
||||||
*/
|
*/
|
||||||
|| ( !reset_selinux
|
|| ( !reset_selinux
|
||||||
&& (attr_copy_file (src, dst, NULL, &ctx) != 0)
|
&& (attr_copy_path (src, dst, NULL, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#endif /* WITH_ATTR */
|
#endif /* WITH_ATTR */
|
||||||
|| (utimes (dst, mt) != 0)) {
|
|| (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) {
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* full_write - write entire buffer
|
||||||
|
*
|
||||||
|
* Write up to count bytes from the buffer starting at buf to the
|
||||||
|
* file referred to by the file descriptor fd.
|
||||||
|
* Retry in case of a short write.
|
||||||
|
*
|
||||||
|
* Returns the number of bytes written on success, -1 on error.
|
||||||
|
*/
|
||||||
|
static ssize_t full_write(int fd, const void *buf, size_t count) {
|
||||||
|
ssize_t written = 0;
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
ssize_t res;
|
||||||
|
|
||||||
|
res = write(fd, buf, count);
|
||||||
|
if (res < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
written += res;
|
||||||
|
buf = (const unsigned char*)buf + res;
|
||||||
|
count -= (size_t)res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy_file - copy a file
|
* copy_file - copy a file
|
||||||
*
|
*
|
||||||
@@ -707,34 +790,32 @@ static int copy_special (const char *src, const char *dst,
|
|||||||
*
|
*
|
||||||
* Return 0 on success, -1 on error.
|
* Return 0 on success, -1 on error.
|
||||||
*/
|
*/
|
||||||
static int copy_file (const char *src, const char *dst,
|
static int copy_file (const struct path_info *src, const struct path_info *dst,
|
||||||
bool reset_selinux,
|
bool reset_selinux,
|
||||||
const struct stat *statp, const struct timeval mt[],
|
const struct stat *statp, const struct timespec mt[],
|
||||||
uid_t old_uid, uid_t new_uid,
|
uid_t old_uid, uid_t new_uid,
|
||||||
gid_t old_gid, gid_t new_gid)
|
gid_t old_gid, gid_t new_gid)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
int ifd;
|
int ifd;
|
||||||
int ofd;
|
int ofd;
|
||||||
char buf[1024];
|
|
||||||
ssize_t cnt;
|
|
||||||
|
|
||||||
ifd = open (src, O_RDONLY|O_NOFOLLOW);
|
ifd = openat (src->dirfd, src->name, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
|
||||||
if (ifd < 0) {
|
if (ifd < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#ifdef WITH_SELINUX
|
#ifdef WITH_SELINUX
|
||||||
if (set_selinux_file_context (dst, S_IFREG) != 0) {
|
if (set_selinux_file_context (dst->full_path, S_IFREG) != 0) {
|
||||||
(void) close (ifd);
|
(void) close (ifd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif /* WITH_SELINUX */
|
#endif /* WITH_SELINUX */
|
||||||
ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, statp->st_mode & 07777);
|
ofd = openat (dst->dirfd, dst->name, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, statp->st_mode & 07777);
|
||||||
if ( (ofd < 0)
|
if ( (ofd < 0)
|
||||||
|| (fchown_if_needed (ofd, statp,
|
|| (fchown_if_needed (ofd, statp,
|
||||||
old_uid, new_uid, old_gid, new_gid) != 0)
|
old_uid, new_uid, old_gid, new_gid) != 0)
|
||||||
#ifdef WITH_ACL
|
#ifdef WITH_ACL
|
||||||
|| ( (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0)
|
|| ( (perm_copy_fd (src->full_path, ifd, dst->full_path, ofd, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#else /* !WITH_ACL */
|
#else /* !WITH_ACL */
|
||||||
|| (fchmod (ofd, statp->st_mode & 07777) != 0)
|
|| (fchmod (ofd, statp->st_mode & 07777) != 0)
|
||||||
@@ -748,7 +829,7 @@ static int copy_file (const char *src, const char *dst,
|
|||||||
* additional logic so that no unexpected permissions result.
|
* additional logic so that no unexpected permissions result.
|
||||||
*/
|
*/
|
||||||
|| ( !reset_selinux
|
|| ( !reset_selinux
|
||||||
&& (attr_copy_fd (src, ifd, dst, ofd, NULL, &ctx) != 0)
|
&& (attr_copy_fd (src->full_path, ifd, dst->full_path, ofd, NULL, &ctx) != 0)
|
||||||
&& (errno != 0))
|
&& (errno != 0))
|
||||||
#endif /* WITH_ATTR */
|
#endif /* WITH_ATTR */
|
||||||
) {
|
) {
|
||||||
@@ -759,8 +840,24 @@ static int copy_file (const char *src, const char *dst,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
|
while (true) {
|
||||||
if (write (ofd, buf, (size_t)cnt) != cnt) {
|
char buf[8192];
|
||||||
|
ssize_t cnt;
|
||||||
|
|
||||||
|
cnt = read (ifd, buf, sizeof buf);
|
||||||
|
if (cnt < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(void) close (ofd);
|
||||||
|
(void) close (ifd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (cnt == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_write (ofd, buf, (size_t)cnt) < 0) {
|
||||||
(void) close (ofd);
|
(void) close (ofd);
|
||||||
(void) close (ifd);
|
(void) close (ifd);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -768,23 +865,13 @@ static int copy_file (const char *src, const char *dst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
(void) close (ifd);
|
(void) close (ifd);
|
||||||
|
|
||||||
#ifdef HAVE_FUTIMES
|
|
||||||
if (futimes (ofd, mt) != 0) {
|
|
||||||
(void) close (ofd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif /* HAVE_FUTIMES */
|
|
||||||
|
|
||||||
if (close (ofd) != 0) {
|
if (close (ofd) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef HAVE_FUTIMES
|
if (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0) {
|
||||||
if (utimes(dst, mt) != 0) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif /* !HAVE_FUTIMES */
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -819,7 +906,70 @@ static int chown_function ## _if_needed (type_dst dst, \
|
|||||||
return chown_function (dst, tmpuid, tmpgid); \
|
return chown_function (dst, tmpuid, tmpgid); \
|
||||||
}
|
}
|
||||||
|
|
||||||
def_chown_if_needed (chown, const char *)
|
|
||||||
def_chown_if_needed (lchown, const char *)
|
|
||||||
def_chown_if_needed (fchown, int)
|
def_chown_if_needed (fchown, int)
|
||||||
|
|
||||||
|
static int chownat_if_needed (const struct path_info *dst,
|
||||||
|
const struct stat *statp,
|
||||||
|
uid_t old_uid, uid_t new_uid,
|
||||||
|
gid_t old_gid, gid_t new_gid)
|
||||||
|
{
|
||||||
|
uid_t tmpuid = (uid_t) -1;
|
||||||
|
gid_t tmpgid = (gid_t) -1;
|
||||||
|
|
||||||
|
/* Use new_uid if old_uid is set to -1 or if the file was
|
||||||
|
* owned by the user. */
|
||||||
|
if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) {
|
||||||
|
tmpuid = new_uid;
|
||||||
|
}
|
||||||
|
/* Otherwise, or if new_uid was set to -1, we keep the same
|
||||||
|
* owner. */
|
||||||
|
if ((uid_t) -1 == tmpuid) {
|
||||||
|
tmpuid = statp->st_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) {
|
||||||
|
tmpgid = new_gid;
|
||||||
|
}
|
||||||
|
if ((gid_t) -1 == tmpgid) {
|
||||||
|
tmpgid = statp->st_gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fchownat (dst->dirfd, dst->name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* copy_tree - copy files in a directory tree
|
||||||
|
*
|
||||||
|
* copy_tree() walks a directory tree and copies ordinary files
|
||||||
|
* as it goes.
|
||||||
|
*
|
||||||
|
* When reset_selinux is enabled, extended attributes (and thus
|
||||||
|
* SELinux attributes) are not copied.
|
||||||
|
*
|
||||||
|
* old_uid and new_uid are used to set the ownership of the copied
|
||||||
|
* files. Unless old_uid is set to -1, only the files owned by
|
||||||
|
* old_uid have their ownership changed to new_uid. In addition, if
|
||||||
|
* new_uid is set to -1, no ownership will be changed.
|
||||||
|
*
|
||||||
|
* The same logic applies for the group-ownership and
|
||||||
|
* old_gid/new_gid.
|
||||||
|
*/
|
||||||
|
int copy_tree (const char *src_root, const char *dst_root,
|
||||||
|
bool copy_root, bool reset_selinux,
|
||||||
|
uid_t old_uid, uid_t new_uid,
|
||||||
|
gid_t old_gid, gid_t new_gid)
|
||||||
|
{
|
||||||
|
const struct path_info src = {
|
||||||
|
.full_path = src_root,
|
||||||
|
.dirfd = AT_FDCWD,
|
||||||
|
.name = src_root
|
||||||
|
};
|
||||||
|
const struct path_info dst = {
|
||||||
|
.full_path = dst_root,
|
||||||
|
.dirfd = AT_FDCWD,
|
||||||
|
.name = dst_root
|
||||||
|
};
|
||||||
|
|
||||||
|
return copy_tree_impl(&src, &dst, copy_root, reset_selinux,
|
||||||
|
old_uid, new_uid, old_gid, new_gid);
|
||||||
|
}
|
||||||
|
|||||||
+68
-77
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#ident "$Id$"
|
#ident "$Id$"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -21,6 +22,72 @@
|
|||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
|
static int remove_tree_at (int at_fd, const char *path, bool remove_root)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
const struct dirent *ent;
|
||||||
|
int dir_fd, rc = 0;
|
||||||
|
|
||||||
|
dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||||
|
if (dir_fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = fdopendir (dir_fd);
|
||||||
|
if (!dir) {
|
||||||
|
(void) close (dir_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the source directory and delete each entry.
|
||||||
|
*/
|
||||||
|
while ((ent = readdir (dir))) {
|
||||||
|
struct stat ent_sb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip the "." and ".." entries
|
||||||
|
*/
|
||||||
|
if (strcmp (ent->d_name, ".") == 0 ||
|
||||||
|
strcmp (ent->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW);
|
||||||
|
if (rc < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR (ent_sb.st_mode)) {
|
||||||
|
/*
|
||||||
|
* Recursively delete this directory.
|
||||||
|
*/
|
||||||
|
if (remove_tree_at (dirfd(dir), ent->d_name, true) != 0) {
|
||||||
|
rc = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Delete the file.
|
||||||
|
*/
|
||||||
|
if (unlinkat (dirfd(dir), ent->d_name, 0) != 0) {
|
||||||
|
rc = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) closedir (dir);
|
||||||
|
|
||||||
|
if (remove_root && (0 == rc)) {
|
||||||
|
if (unlinkat (at_fd, path, AT_REMOVEDIR) != 0) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* remove_tree - delete a directory tree
|
* remove_tree - delete a directory tree
|
||||||
*
|
*
|
||||||
@@ -28,83 +95,7 @@
|
|||||||
* and directories.
|
* and directories.
|
||||||
* At the end, it deletes the root directory itself.
|
* At the end, it deletes the root directory itself.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int remove_tree (const char *root, bool remove_root)
|
int remove_tree (const char *root, bool remove_root)
|
||||||
{
|
{
|
||||||
char *new_name = NULL;
|
return remove_tree_at (AT_FDCWD, root, remove_root);
|
||||||
int err = 0;
|
|
||||||
struct dirent *ent;
|
|
||||||
struct stat sb;
|
|
||||||
DIR *dir;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open the source directory and read each entry. Every file
|
|
||||||
* entry in the directory is copied with the UID and GID set
|
|
||||||
* to the provided values. As an added security feature only
|
|
||||||
* regular files (and directories ...) are copied, and no file
|
|
||||||
* is made set-ID.
|
|
||||||
*/
|
|
||||||
dir = opendir (root);
|
|
||||||
if (NULL == dir) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = readdir (dir))) {
|
|
||||||
size_t new_len = strlen (root) + strlen (ent->d_name) + 2;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip the "." and ".." entries
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (strcmp (ent->d_name, ".") == 0 ||
|
|
||||||
strcmp (ent->d_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make the filename for the current entry.
|
|
||||||
*/
|
|
||||||
|
|
||||||
free (new_name);
|
|
||||||
new_name = (char *) malloc (new_len);
|
|
||||||
if (NULL == new_name) {
|
|
||||||
err = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
(void) snprintf (new_name, new_len, "%s/%s", root, ent->d_name);
|
|
||||||
if (LSTAT (new_name, &sb) == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (S_ISDIR (sb.st_mode)) {
|
|
||||||
/*
|
|
||||||
* Recursively delete this directory.
|
|
||||||
*/
|
|
||||||
if (remove_tree (new_name, true) != 0) {
|
|
||||||
err = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Delete the file.
|
|
||||||
*/
|
|
||||||
if (unlink (new_name) != 0) {
|
|
||||||
err = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (NULL != new_name) {
|
|
||||||
free (new_name);
|
|
||||||
}
|
|
||||||
(void) closedir (dir);
|
|
||||||
|
|
||||||
if (remove_root && (0 == err)) {
|
|
||||||
if (rmdir (root) != 0) {
|
|
||||||
err = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user