/* vi: set sw=4 ts=4: */ /* * Utility routines. * * Copyright (C) tons of folks. Tracking down who wrote what * isn't something I'm going to worry about... If you wrote something * here, please feel free to acknowledge your work. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Based in part on code from sash, Copyright (c) 1999 by David I. Bell * Permission has been granted to redistribute this code under the GPL. * */ #include #include #include #include #include #include #include "libbb.h" /* * Copy one file to another, while possibly preserving its modes, times, and * modes. Returns TRUE if successful, or FALSE on a failure with an error * message output. (Failure is not indicated if attributes cannot be set.) * -Erik Andersen */ int copy_file(const char *srcName, const char *destName, int setModes, int followLinks, int forceFlag) { int rfd; int wfd; int status; struct stat srcStatBuf; struct stat dstStatBuf; struct utimbuf times; if (followLinks == TRUE) status = stat(srcName, &srcStatBuf); else status = lstat(srcName, &srcStatBuf); if (status < 0) { perror_msg("%s", srcName); return FALSE; } if (followLinks == TRUE) status = stat(destName, &dstStatBuf); else status = lstat(destName, &dstStatBuf); if (status < 0 || forceFlag==TRUE) { unlink(destName); dstStatBuf.st_ino = -1; dstStatBuf.st_dev = -1; } if ((srcStatBuf.st_dev == dstStatBuf.st_dev) && (srcStatBuf.st_ino == dstStatBuf.st_ino)) { error_msg("Copying file \"%s\" to itself", srcName); return FALSE; } if (S_ISDIR(srcStatBuf.st_mode)) { //fprintf(stderr, "copying directory %s to %s\n", srcName, destName); /* Make sure the directory is writable */ status = mkdir(destName, 0777777 ^ umask(0)); if (status < 0 && errno != EEXIST) { perror_msg("%s", destName); return FALSE; } } else if (S_ISLNK(srcStatBuf.st_mode)) { char link_val[BUFSIZ + 1]; int link_size; //fprintf(stderr, "copying link %s to %s\n", srcName, destName); /* Warning: This could possibly truncate silently, to BUFSIZ chars */ link_size = readlink(srcName, &link_val[0], BUFSIZ); if (link_size < 0) { perror_msg("%s", srcName); return FALSE; } link_val[link_size] = '\0'; status = symlink(link_val, destName); if (status < 0) { perror_msg("%s", destName); return FALSE; } #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) if (setModes == TRUE) { /* Try to set owner, but fail silently like GNU cp */ lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid); } #endif return TRUE; } else if (S_ISFIFO(srcStatBuf.st_mode)) { //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName); if (mkfifo(destName, 0644) < 0) { perror_msg("%s", destName); return FALSE; } } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) || S_ISSOCK(srcStatBuf.st_mode)) { //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName); if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev) < 0) { perror_msg("%s", destName); return FALSE; } } else if (S_ISREG(srcStatBuf.st_mode)) { //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName); rfd = open(srcName, O_RDONLY); if (rfd < 0) { perror_msg("%s", srcName); return FALSE; } wfd = open(destName, O_WRONLY | O_CREAT | O_TRUNC, srcStatBuf.st_mode); if (wfd < 0) { perror_msg("%s", destName); close(rfd); return FALSE; } if (copy_file_chunk(rfd, wfd, srcStatBuf.st_size)==FALSE) goto error_exit; close(rfd); if (close(wfd) < 0) { return FALSE; } } if (setModes == TRUE) { /* This is fine, since symlinks never get here */ if (chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid) < 0) perror_msg_and_die("%s", destName); if (chmod(destName, srcStatBuf.st_mode) < 0) perror_msg_and_die("%s", destName); times.actime = srcStatBuf.st_atime; times.modtime = srcStatBuf.st_mtime; if (utime(destName, ×) < 0) perror_msg_and_die("%s", destName); } return TRUE; error_exit: perror_msg("%s", destName); close(rfd); close(wfd); return FALSE; } /* END CODE */ /* Local Variables: c-file-style: "linux" c-basic-offset: 4 tab-width: 4 End: */