| /* vi: set sw=4 ts=4: */ |
| /* |
| * Mini `cp' and `mv' implementation for BusyBox. |
| * |
| * |
| * Copyright (C) 1999,2000,2001 by Lineo, inc. |
| * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> |
| * |
| * Copyright (C) 2000 by BitterSweet Enterprises, LLC. (GPL) |
| * Extensively modified and rewritten by Karl M. Hegbloom <karlheg@debian.org> |
| * |
| * 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 |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <time.h> |
| #include <utime.h> |
| #include <dirent.h> |
| #include <sys/param.h> |
| #include <setjmp.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <stdlib.h> |
| #include "busybox.h" |
| #define BB_DECLARE_EXTERN |
| #define bb_need_name_too_long |
| #define bb_need_omitting_directory |
| #define bb_need_not_a_directory |
| #include "messages.c" |
| |
| |
| static const int is_cp = 0; |
| static const int is_mv = 1; |
| static int dz_i; /* index into cp_mv_usage */ |
| |
| |
| static int recursiveFlag; |
| static int followLinks; |
| static int preserveFlag; |
| static int forceFlag; |
| |
| static const char *baseSrcName; |
| static int srcDirFlag; |
| static struct stat srcStatBuf; |
| |
| static char *pBaseDestName; |
| static size_t baseDestLen; |
| static int destDirFlag; |
| static struct stat destStatBuf; |
| |
| static jmp_buf catch; |
| static volatile int mv_Action_first_time; |
| |
| static void name_too_long__exit (void) __attribute__((noreturn)); |
| |
| static |
| void name_too_long__exit (void) |
| { |
| error_msg_and_die(name_too_long); |
| } |
| |
| static void |
| fill_baseDest_buf(char *_buf, size_t * _buflen) { |
| const char *srcBasename; |
| if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { |
| srcBasename = baseSrcName; |
| if (_buf[*_buflen - 1] != '/') { |
| if (++(*_buflen) > BUFSIZ) |
| name_too_long__exit(); |
| strcat(_buf, "/"); |
| } |
| } |
| if (*_buflen + strlen(srcBasename) > BUFSIZ) |
| name_too_long__exit(); |
| strcat(_buf, srcBasename); |
| return; |
| |
| } |
| |
| static int |
| cp_mv_Action(const char *fileName, struct stat *statbuf, void* junk) |
| { |
| char destName[BUFSIZ + 1]; |
| size_t destLen; |
| const char *srcBasename; |
| char *name; |
| |
| strcpy(destName, pBaseDestName); |
| destLen = strlen(destName); |
| |
| if (srcDirFlag == TRUE) { |
| if (recursiveFlag == FALSE) { |
| error_msg(omitting_directory, baseSrcName); |
| return TRUE; |
| } |
| srcBasename = (strstr(fileName, baseSrcName) |
| + strlen(baseSrcName)); |
| |
| if (destLen + strlen(srcBasename) > BUFSIZ) { |
| error_msg(name_too_long); |
| return FALSE; |
| } |
| strcat(destName, srcBasename); |
| } |
| else if (destDirFlag == TRUE) { |
| fill_baseDest_buf(&destName[0], &destLen); |
| } |
| else { |
| srcBasename = baseSrcName; |
| } |
| if (mv_Action_first_time && (dz_i == is_mv)) { |
| mv_Action_first_time = errno = 0; |
| if (rename(fileName, destName) < 0 && errno != EXDEV) { |
| perror_msg("rename(%s, %s)", fileName, destName); |
| goto do_copyFile; /* Try anyway... */ |
| } |
| else if (errno == EXDEV) |
| goto do_copyFile; |
| else |
| longjmp(catch, 1); /* succeeded with rename() */ |
| } |
| do_copyFile: |
| if (preserveFlag == TRUE && statbuf->st_nlink > 1) { |
| if (is_in_ino_dev_hashtable(statbuf, &name)) { |
| if (link(name, destName) < 0) { |
| perror_msg("link(%s, %s)", name, destName); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| else { |
| add_to_ino_dev_hashtable(statbuf, destName); |
| } |
| } |
| return copy_file(fileName, destName, preserveFlag, followLinks, forceFlag); |
| } |
| |
| static int |
| rm_Action(const char *fileName, struct stat *statbuf, void* junk) |
| { |
| int status = TRUE; |
| |
| if (S_ISDIR(statbuf->st_mode)) { |
| if (rmdir(fileName) < 0) { |
| perror_msg("rmdir(%s)", fileName); |
| status = FALSE; |
| } |
| } else if (unlink(fileName) < 0) { |
| perror_msg("unlink(%s)", fileName); |
| status = FALSE; |
| } |
| return status; |
| } |
| |
| extern int cp_mv_main(int argc, char **argv) |
| { |
| volatile int i; |
| int c; |
| RESERVE_BB_BUFFER(baseDestName,BUFSIZ + 1); |
| pBaseDestName = baseDestName; /* available globally */ |
| |
| if (*applet_name == 'c' && *(applet_name + 1) == 'p') |
| dz_i = is_cp; |
| else |
| dz_i = is_mv; |
| if (argc < 3) |
| show_usage(); |
| |
| if (dz_i == is_cp) { |
| recursiveFlag = preserveFlag = forceFlag = FALSE; |
| followLinks = TRUE; |
| while ((c = getopt(argc, argv, "adpRf")) != EOF) { |
| switch (c) { |
| case 'a': |
| followLinks = FALSE; |
| preserveFlag = TRUE; |
| recursiveFlag = TRUE; |
| break; |
| case 'd': |
| followLinks = FALSE; |
| break; |
| case 'p': |
| preserveFlag = TRUE; |
| break; |
| case 'R': |
| recursiveFlag = TRUE; |
| break; |
| case 'f': |
| forceFlag = TRUE; |
| break; |
| default: |
| show_usage(); |
| } |
| } |
| if ((argc - optind) < 2) { |
| show_usage(); |
| } |
| } else { /* (dz_i == is_mv) */ |
| /* Initialize optind to 1, since in libc5 optind |
| * is not initialized until getopt() is called |
| * (or until sneaky programmers force it...). */ |
| optind = 1; |
| recursiveFlag = preserveFlag = TRUE; |
| followLinks = FALSE; |
| } |
| |
| |
| if (strlen(argv[argc - 1]) > BUFSIZ) { |
| error_msg(name_too_long); |
| goto exit_false; |
| } |
| strcpy(baseDestName, argv[argc - 1]); |
| baseDestLen = strlen(baseDestName); |
| if (baseDestLen == 0) |
| goto exit_false; |
| |
| destDirFlag = is_directory(baseDestName, TRUE, &destStatBuf); |
| if (argc - optind > 2 && destDirFlag == FALSE) { |
| error_msg(not_a_directory, baseDestName); |
| goto exit_false; |
| } |
| |
| for (i = optind; i < (argc-1); i++) { |
| size_t srcLen; |
| volatile int flags_memo; |
| int status; |
| |
| baseSrcName=argv[i]; |
| |
| if ((srcLen = strlen(baseSrcName)) > BUFSIZ) |
| name_too_long__exit(); |
| |
| if (srcLen == 0) continue; /* "" */ |
| |
| srcDirFlag = is_directory(baseSrcName, followLinks, &srcStatBuf); |
| |
| if ((flags_memo = (recursiveFlag == TRUE && |
| srcDirFlag == TRUE && destDirFlag == TRUE))) { |
| |
| struct stat sb; |
| int state = 0; |
| char *pushd, *d, *p; |
| |
| if ((pushd = xgetcwd(0)) == NULL) |
| continue; |
| |
| if (chdir(baseDestName) < 0) { |
| perror_msg("chdir(%s)", baseSrcName); |
| continue; |
| } |
| if ((d = xgetcwd(0)) == NULL) |
| continue; |
| |
| while (!state && *d != '\0') { |
| if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */ |
| perror_msg("stat(%s)", d); |
| state = -1; |
| continue; |
| } |
| if ((sb.st_ino == srcStatBuf.st_ino) && |
| (sb.st_dev == srcStatBuf.st_dev)) { |
| error_msg("Cannot %s `%s' into a subdirectory of itself, " |
| "`%s/%s'", applet_name, baseSrcName, |
| baseDestName, baseSrcName); |
| state = -1; |
| continue; |
| } |
| if ((p = strrchr(d, '/')) != NULL) { |
| *p = '\0'; |
| } |
| } |
| if (chdir(pushd) < 0) { |
| perror_msg("chdir(%s)", pushd); |
| free(pushd); |
| free(d); |
| continue; |
| } |
| free(pushd); |
| free(d); |
| if (state < 0) |
| continue; |
| else |
| fill_baseDest_buf(baseDestName, &baseDestLen); |
| } |
| status = setjmp(catch); |
| if (status == 0) { |
| mv_Action_first_time = 1; |
| if (recursive_action(baseSrcName, |
| recursiveFlag, followLinks, FALSE, |
| cp_mv_Action, cp_mv_Action, NULL) == FALSE) goto exit_false; |
| if (dz_i == is_mv && |
| recursive_action(baseSrcName, |
| recursiveFlag, followLinks, TRUE, |
| rm_Action, rm_Action, NULL) == FALSE) goto exit_false; |
| } |
| if (flags_memo) |
| *(baseDestName + baseDestLen) = '\0'; |
| } |
| return EXIT_SUCCESS; |
| exit_false: |
| return EXIT_FAILURE; |
| } |
| |
| /* |
| Local Variables: |
| c-file-style: "linux" |
| c-basic-offset: 4 |
| tab-width: 4 |
| End: |
| */ |