diff options
Diffstat (limited to '')
-rw-r--r-- | lib/copydir.c (renamed from libmisc/copydir.c) | 333 |
1 files changed, 145 insertions, 188 deletions
diff --git a/libmisc/copydir.c b/lib/copydir.c index b692aa9..926033a 100644 --- a/libmisc/copydir.c +++ b/lib/copydir.c @@ -17,6 +17,9 @@ #include <sys/time.h> #include <fcntl.h> #include <stdio.h> + +#include "alloc.h" +#include "attr.h" #include "prototypes.h" #include "defines.h" #ifdef WITH_SELINUX @@ -33,6 +36,7 @@ #include <attr/libattr.h> #endif /* WITH_ATTR */ #include "shadowlog.h" +#include "string/sprintf.h" static /*@null@*/const char *src_orig; @@ -64,12 +68,12 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst, gid_t old_gid, gid_t new_gid); static /*@null@*/char *readlink_malloc (const char *filename); static int copy_symlink (const struct path_info *src, const struct path_info *dst, - unused bool reset_selinux, + MAYBE_UNUSED bool reset_selinux, const struct stat *statp, const struct timespec mt[], uid_t old_uid, uid_t new_uid, gid_t old_gid, gid_t new_gid); static int copy_hardlink (const struct path_info *dst, - unused bool reset_selinux, + MAYBE_UNUSED bool reset_selinux, struct link_name *lp); static int copy_special (const struct path_info *src, const struct path_info *dst, bool reset_selinux, @@ -93,7 +97,7 @@ static int fchown_if_needed (int fdst, const struct stat *statp, * error_acl - format the error messages for the ACL and EQ libraries. */ format_attr(printf, 2, 3) -static void error_acl (unused struct error_context *ctx, const char *fmt, ...) +static void error_acl (MAYBE_UNUSED struct error_context *ctx, const char *fmt, ...) { va_list ap; FILE *shadow_logfd = log_get_logfd(); @@ -206,11 +210,7 @@ static void remove_link (/*@only@*/struct link_name *ln) static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, const struct stat *sb) { - struct link_name *lp; - size_t src_len; - size_t dst_len; - size_t name_len; - size_t len; + struct link_name *lp; /* copy_tree () must be the entry point */ assert (NULL != src_orig); @@ -226,16 +226,11 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c return NULL; } - lp = (struct link_name *) xmalloc (sizeof *lp); - src_len = strlen (src_orig); - dst_len = strlen (dst_orig); - name_len = strlen (name); + lp = XMALLOC(1, struct link_name); lp->ln_dev = sb->st_dev; lp->ln_ino = sb->st_ino; lp->ln_count = sb->st_nlink; - len = name_len - src_len + dst_len + 1; - lp->ln_name = (char *) xmalloc (len); - (void) snprintf (lp->ln_name, len, "%s%s", dst_orig, name + src_len); + xasprintf(&lp->ln_name, "%s%s", dst_orig, name + strlen(src_orig)); lp->ln_next = links; links = lp; @@ -312,51 +307,43 @@ static int copy_tree_impl (const struct path_info *src, const struct path_info * set_orig = true; } while ((0 == err) && (ent = readdir (dir)) != NULL) { + char *src_name = NULL; + char *dst_name; + struct path_info src_entry, dst_entry; /* * Skip the "." and ".." entries */ - if ((strcmp (ent->d_name, ".") != 0) && - (strcmp (ent->d_name, "..") != 0)) { - char *src_name; - char *dst_name; - size_t src_len = strlen (ent->d_name) + 2; - size_t dst_len = strlen (ent->d_name) + 2; - src_len += strlen (src->full_path); - dst_len += strlen (dst->full_path); - - src_name = (char *) malloc (src_len); - dst_name = (char *) malloc (dst_len); - - if ((NULL == src_name) || (NULL == dst_name)) { - err = -1; - } else { - /* - * Build the filename for both the source and - * the destination files. - */ - struct path_info src_entry, dst_entry; - - (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, - old_uid, new_uid, - old_gid, new_gid); - } - free (src_name); - free (dst_name); + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + { + continue; + } + + if (asprintf(&src_name, "%s/%s", src->full_path, ent->d_name) == -1) + { + err = -1; + continue; + } + if (asprintf(&dst_name, "%s/%s", dst->full_path, ent->d_name) == -1) + { + err = -1; + goto skip; } + + 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, + old_uid, new_uid, old_gid, new_gid); + + free(dst_name); +skip: + free(src_name); } (void) closedir (dir); (void) close (dst_fd); @@ -413,78 +400,70 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst, { int err = 0; struct stat sb; + struct stat tmp_sb; struct link_name *lp; struct timespec mt[2]; if (fstatat(src->dirfd, src->name, &sb, AT_SYMLINK_NOFOLLOW) == -1) { /* If we cannot stat the file, do not care. */ - } else { -#ifdef HAVE_STRUCT_STAT_ST_ATIM - mt[0].tv_sec = sb.st_atim.tv_sec; - mt[0].tv_nsec = sb.st_atim.tv_nsec; -#else /* !HAVE_STRUCT_STAT_ST_ATIM */ - mt[0].tv_sec = sb.st_atime; -# ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC - mt[0].tv_nsec = sb.st_atimensec; -# else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ - mt[0].tv_nsec = 0; -# endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ -#endif /* !HAVE_STRUCT_STAT_ST_ATIM */ - -#ifdef HAVE_STRUCT_STAT_ST_MTIM - mt[1].tv_sec = sb.st_mtim.tv_sec; - mt[1].tv_nsec = sb.st_mtim.tv_nsec; -#else /* !HAVE_STRUCT_STAT_ST_MTIM */ - mt[1].tv_sec = sb.st_mtime; -# ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC - mt[1].tv_nsec = sb.st_mtimensec; -# else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ - mt[1].tv_nsec = 0; -# endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ -#endif /* !HAVE_STRUCT_STAT_ST_MTIM */ - - if (S_ISDIR (sb.st_mode)) { - err = copy_dir (src, dst, reset_selinux, &sb, mt, - old_uid, new_uid, old_gid, new_gid); - } + return 0; + } - /* - * Copy any symbolic links - */ + mt[0].tv_sec = sb.st_atim.tv_sec; + mt[0].tv_nsec = sb.st_atim.tv_nsec; - else if (S_ISLNK (sb.st_mode)) { - err = copy_symlink (src, dst, reset_selinux, &sb, mt, - old_uid, new_uid, old_gid, new_gid); - } + mt[1].tv_sec = sb.st_mtim.tv_sec; + mt[1].tv_nsec = sb.st_mtim.tv_nsec; - /* - * See if this is a previously copied link - */ + if (S_ISDIR (sb.st_mode)) { + err = copy_dir (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } - else if ((lp = check_link (src->full_path, &sb)) != NULL) { - err = copy_hardlink (dst, reset_selinux, lp); - } + /* + * If the destination already exists do nothing. + * This is after the copy_dir above to still iterate into subdirectories. + */ + if (fstatat(dst->dirfd, dst->name, &tmp_sb, AT_SYMLINK_NOFOLLOW) != -1) { + return err; + } - /* - * Deal with FIFOs and special files. The user really - * shouldn't have any of these, but it seems like it - * would be nice to copy everything ... - */ + /* + * Copy any symbolic links + */ - else if (!S_ISREG (sb.st_mode)) { - err = copy_special (src, dst, reset_selinux, &sb, mt, - old_uid, new_uid, old_gid, new_gid); - } + else if (S_ISLNK (sb.st_mode)) { + err = copy_symlink (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } - /* - * Create the new file and copy the contents. The new - * file will be owned by the provided UID and GID values. - */ + /* + * See if this is a previously copied link + */ - else { - err = copy_file (src, dst, reset_selinux, &sb, mt, - old_uid, new_uid, old_gid, new_gid); - } + else if ((lp = check_link (src->full_path, &sb)) != NULL) { + err = copy_hardlink (dst, reset_selinux, lp); + } + + /* + * Deal with FIFOs and special files. The user really + * shouldn't have any of these, but it seems like it + * would be nice to copy everything ... + */ + + else if (!S_ISREG (sb.st_mode)) { + err = copy_special (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); + } + + /* + * Create the new file and copy the contents. The new + * file will be owned by the provided UID and GID values. + */ + + else { + err = copy_file (src, dst, reset_selinux, &sb, mt, + old_uid, new_uid, old_gid, new_gid); } return err; @@ -507,6 +486,7 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst, gid_t old_gid, gid_t new_gid) { int err = 0; + struct stat dst_sb; /* * Create a new target directory, make it owned by @@ -518,6 +498,15 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst, return -1; } #endif /* WITH_SELINUX */ + /* + * If the destination is already a directory, don't change it + * but copy into it (recursively). + */ + if (fstatat(dst->dirfd, dst->name, &dst_sb, AT_SYMLINK_NOFOLLOW) == 0 && S_ISDIR(dst_sb.st_mode)) { + return (copy_tree_impl (src, dst, false, reset_selinux, + old_uid, new_uid, old_gid, new_gid) != 0); + } + if ( (mkdirat (dst->dirfd, dst->name, 0700) != 0) || (chownat_if_needed (dst, statp, old_uid, new_uid, old_gid, new_gid) != 0) @@ -559,7 +548,7 @@ static /*@null@*/char *readlink_malloc (const char *filename) while (true) { ssize_t nchars; - char *buffer = (char *) malloc (size); + char *buffer = MALLOC(size, char); if (NULL == buffer) { return NULL; } @@ -594,7 +583,7 @@ static /*@null@*/char *readlink_malloc (const char *filename) * Return 0 on success, -1 on error. */ static int copy_symlink (const struct path_info *src, const struct path_info *dst, - unused bool reset_selinux, + MAYBE_UNUSED bool reset_selinux, const struct stat *statp, const struct timespec mt[], uid_t old_uid, uid_t new_uid, gid_t old_gid, gid_t new_gid) @@ -622,13 +611,11 @@ static int copy_symlink (const struct path_info *src, const struct path_info *ds * create a link to the corresponding entry in the dst_orig * directory. */ - if (strncmp (oldlink, src_orig, strlen (src_orig)) == 0) { - size_t len = strlen (dst_orig) + strlen (oldlink) - strlen (src_orig) + 1; - char *dummy = (char *) xmalloc (len); - (void) snprintf (dummy, len, "%s%s", - dst_orig, - oldlink + strlen (src_orig)); - free (oldlink); + if (strncmp(oldlink, src_orig, strlen(src_orig)) == 0) { + char *dummy; + + xasprintf(&dummy, "%s%s", dst_orig, oldlink + strlen(src_orig)); + free(oldlink); oldlink = dummy; } @@ -668,7 +655,7 @@ static int copy_symlink (const struct path_info *src, const struct path_info *ds * Return 0 on success, -1 on error. */ static int copy_hardlink (const struct path_info *dst, - unused bool reset_selinux, + MAYBE_UNUSED bool reset_selinux, struct link_name *lp) { /* FIXME: selinux, ACL, Extended Attributes needed? */ @@ -687,6 +674,7 @@ static int copy_hardlink (const struct path_info *dst, return 0; } + /* * copy_special - copy a special file * @@ -697,29 +685,33 @@ static int copy_hardlink (const struct path_info *dst, * * Return 0 on success, -1 on error. */ -static int copy_special (const struct path_info *src, const struct path_info *dst, - bool reset_selinux, - const struct stat *statp, const struct timespec mt[], - uid_t old_uid, uid_t new_uid, - gid_t old_gid, gid_t new_gid) +static int +copy_special(const struct path_info *src, const struct path_info *dst, + bool reset_selinux, + const struct stat *statp, const struct timespec mt[], + uid_t old_uid, uid_t new_uid, + gid_t old_gid, gid_t new_gid) { - int err = 0; +#if defined(WITH_SELINUX) + if (set_selinux_file_context(dst->full_path, statp->st_mode & S_IFMT) != 0) + return -1; +#endif -#ifdef WITH_SELINUX - if (set_selinux_file_context (dst->full_path, statp->st_mode & S_IFMT) != 0) { + if (mknodat(dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) == -1) return -1; - } -#endif /* WITH_SELINUX */ - if ( (mknodat (dst->dirfd, dst->name, statp->st_mode & ~07777U, statp->st_rdev) != 0) - || (chownat_if_needed (dst, statp, - old_uid, new_uid, old_gid, new_gid) != 0) - || (fchmodat (dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) != 0) -#ifdef WITH_ACL - || ( (perm_copy_path (src, dst, &ctx) != 0) - && (errno != 0)) -#endif /* WITH_ACL */ -#ifdef WITH_ATTR + if (chownat_if_needed(dst, statp, old_uid, new_uid, old_gid, new_gid) == -1) + return -1; + + if (fchmodat(dst->dirfd, dst->name, statp->st_mode & 07777, AT_SYMLINK_NOFOLLOW) == -1) + return -1; + +#if defined(WITH_ACL) + if (perm_copy_path(src, dst, &ctx) == -1 && errno != 0) + return -1; +#endif + +#if defined(WITH_ATTR) /* * If the third parameter is NULL, all extended attributes * except those that define Access Control Lists are copied. @@ -727,51 +719,16 @@ static int copy_special (const struct path_info *src, const struct path_info *ds * file systems with and without ACL support needs some * additional logic so that no unexpected permissions result. */ - || ( !reset_selinux - && (attr_copy_path (src, dst, NULL, &ctx) != 0) - && (errno != 0)) -#endif /* WITH_ATTR */ - || (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) { - err = -1; + if (!reset_selinux) { + if (attr_copy_path(src, dst, NULL, &ctx) == -1 && errno != 0) + return -1; } +#endif - 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; - } + if (utimensat(dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) == -1) + return -1; - return written; + return 0; } /* @@ -850,7 +807,7 @@ static int copy_file (const struct path_info *src, const struct path_info *dst, break; } - if (full_write (ofd, buf, (size_t)cnt) < 0) { + if (write_full(ofd, buf, cnt) == -1) { (void) close (ofd); (void) close (ifd); return -1; @@ -858,7 +815,7 @@ static int copy_file (const struct path_info *src, const struct path_info *dst, } (void) close (ifd); - if (close (ofd) != 0) { + if (close (ofd) != 0 && errno != EINTR) { return -1; } |