1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini copy_file implementation for busybox 4 * 5 * 6 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <unistd.h> 27#include <fcntl.h> 28#include <utime.h> 29#include <errno.h> 30#include <dirent.h> 31#include <stdlib.h> 32#include <string.h> 33 34#include "libbb.h" 35 36int copy_file(const char *source, const char *dest, int flags) 37{ 38 struct stat source_stat; 39 struct stat dest_stat; 40 int dest_exists = 1; 41 int status = 0; 42 43 if (((flags & FILEUTILS_PRESERVE_SYMLINKS) && 44 lstat(source, &source_stat) < 0) || 45 (!(flags & FILEUTILS_PRESERVE_SYMLINKS) && 46 stat(source, &source_stat) < 0)) { 47 perror_msg("%s", source); 48 return -1; 49 } 50 51 if (stat(dest, &dest_stat) < 0) { 52 if (errno != ENOENT) { 53 perror_msg("unable to stat `%s'", dest); 54 return -1; 55 } 56 dest_exists = 0; 57 } 58 59 if (dest_exists && source_stat.st_rdev == dest_stat.st_rdev && 60 source_stat.st_ino == dest_stat.st_ino) { 61 error_msg("`%s' and `%s' are the same file", source, dest); 62 return -1; 63 } 64 65 if (S_ISDIR(source_stat.st_mode)) { 66 DIR *dp; 67 struct dirent *d; 68 mode_t saved_umask = 0; 69 70 if (!(flags & FILEUTILS_RECUR)) { 71 error_msg("%s: omitting directory", source); 72 return -1; 73 } 74 75 /* Create DEST. */ 76 if (dest_exists) { 77 if (!S_ISDIR(dest_stat.st_mode)) { 78 error_msg("`%s' is not a directory", dest); 79 return -1; 80 } 81 } else { 82 mode_t mode; 83 saved_umask = umask(0); 84 85 mode = source_stat.st_mode; 86 if (!(flags & FILEUTILS_PRESERVE_STATUS)) 87 mode = source_stat.st_mode & ~saved_umask; 88 mode |= S_IRWXU; 89 90 if (mkdir(dest, mode) < 0) { 91 umask(saved_umask); 92 perror_msg("cannot create directory `%s'", dest); 93 return -1; 94 } 95 96 umask(saved_umask); 97 } 98 99 /* Recursively copy files in SOURCE. */ 100 if ((dp = opendir(source)) == NULL) { 101 perror_msg("unable to open directory `%s'", source); 102 status = -1; 103 goto end; 104 } 105 106 while ((d = readdir(dp)) != NULL) { 107 char *new_source, *new_dest; 108 109 if (strcmp(d->d_name, ".") == 0 || 110 strcmp(d->d_name, "..") == 0) 111 continue; 112 113 new_source = concat_path_file(source, d->d_name); 114 new_dest = concat_path_file(dest, d->d_name); 115 if (copy_file(new_source, new_dest, flags) < 0) 116 status = -1; 117 free(new_source); 118 free(new_dest); 119 } 120 121 /* ??? What if an error occurs in readdir? */ 122 123 if (closedir(dp) < 0) { 124 perror_msg("unable to close directory `%s'", source); 125 status = -1; 126 } 127 128 if (!dest_exists && 129 chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { 130 perror_msg("unable to change permissions of `%s'", dest); 131 status = -1; 132 } 133 } else if (S_ISREG(source_stat.st_mode)) { 134 FILE *sfp, *dfp; 135 136 if (dest_exists) { 137 if (flags & FILEUTILS_INTERACTIVE) { 138 fprintf(stderr, "%s: overwrite `%s'? ", applet_name, dest); 139 if (!ask_confirmation()) 140 return 0; 141 } 142 143 if ((dfp = fopen(dest, "w")) == NULL) { 144 if (!(flags & FILEUTILS_FORCE)) { 145 perror_msg("unable to open `%s'", dest); 146 return -1; 147 } 148 149 if (unlink(dest) < 0) { 150 perror_msg("unable to remove `%s'", dest); 151 return -1; 152 } 153 154 dest_exists = 0; 155 } 156 } 157 158 if (!dest_exists) { 159 int fd; 160 161 if ((fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode)) < 0 || 162 (dfp = fdopen(fd, "w")) == NULL) { 163 if (fd >= 0) 164 close(fd); 165 perror_msg("unable to open `%s'", dest); 166 return -1; 167 } 168 } 169 170 if ((sfp = fopen(source, "r")) == NULL) { 171 fclose(dfp); 172 perror_msg("unable to open `%s'", source); 173 status = -1; 174 goto end; 175 } 176 177 if (copy_file_chunk(sfp, dfp, -1) < 0) 178 status = -1; 179 180 if (fclose(dfp) < 0) { 181 perror_msg("unable to close `%s'", dest); 182 status = -1; 183 } 184 185 if (fclose(sfp) < 0) { 186 perror_msg("unable to close `%s'", source); 187 status = -1; 188 } 189 } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || 190 S_ISSOCK(source_stat.st_mode)) { 191 if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { 192 perror_msg("unable to create `%s'", dest); 193 return -1; 194 } 195 } else if (S_ISFIFO(source_stat.st_mode)) { 196 if (mkfifo(dest, source_stat.st_mode) < 0) { 197 perror_msg("cannot create fifo `%s'", dest); 198 return -1; 199 } 200 } else if (S_ISLNK(source_stat.st_mode)) { 201 char *lpath = xreadlink(source); 202 if (symlink(lpath, dest) < 0) { 203 perror_msg("cannot create symlink `%s'", dest); 204 return -1; 205 } 206 free(lpath); 207 208#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) 209 if (flags & FILEUTILS_PRESERVE_STATUS) 210 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) 211 perror_msg("unable to preserve ownership of `%s'", dest); 212#endif 213 return 0; 214 } else { 215 error_msg("internal error: unrecognized file type"); 216 return -1; 217 } 218 219end: 220 221 if (flags & FILEUTILS_PRESERVE_STATUS) { 222 struct utimbuf times; 223 224 times.actime = source_stat.st_atime; 225 times.modtime = source_stat.st_mtime; 226 if (utime(dest, ×) < 0) 227 perror_msg("unable to preserve times of `%s'", dest); 228 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { 229 source_stat.st_mode &= ~(S_ISUID | S_ISGID); 230 perror_msg("unable to preserve ownership of `%s'", dest); 231 } 232 if (chmod(dest, source_stat.st_mode) < 0) 233 perror_msg("unable to preserve permissions of `%s'", dest); 234 } 235 236 return status; 237} 238