1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini `cp' and `mv' implementation for BusyBox. 4 * 5 * 6 * Copyright (C) 1999,2000,2001 by Lineo, inc. 7 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> 8 * 9 * Copyright (C) 2000 by BitterSweet Enterprises, LLC. (GPL) 10 * Extensively modified and rewritten by Karl M. Hegbloom <karlheg@debian.org> 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 * 26 */ 27 28#include <stdio.h> 29#include <time.h> 30#include <utime.h> 31#include <dirent.h> 32#include <sys/param.h> 33#include <setjmp.h> 34#include <string.h> 35#include <unistd.h> 36#include <errno.h> 37#include <getopt.h> 38#include <stdlib.h> 39#include "busybox.h" 40#define BB_DECLARE_EXTERN 41#define bb_need_name_too_long 42#define bb_need_omitting_directory 43#define bb_need_not_a_directory 44#include "messages.c" 45 46 47static const int is_cp = 0; 48static const int is_mv = 1; 49static int dz_i; /* index into cp_mv_usage */ 50 51 52static int recursiveFlag; 53static int followLinks; 54static int preserveFlag; 55static int forceFlag; 56 57static const char *baseSrcName; 58static int srcDirFlag; 59static struct stat srcStatBuf; 60 61static char *pBaseDestName; 62static size_t baseDestLen; 63static int destDirFlag; 64static struct stat destStatBuf; 65 66static jmp_buf catch; 67static volatile int mv_Action_first_time; 68 69static void name_too_long__exit (void) __attribute__((noreturn)); 70 71static 72void name_too_long__exit (void) 73{ 74 error_msg_and_die(name_too_long); 75} 76 77static void 78fill_baseDest_buf(char *_buf, size_t * _buflen) { 79 const char *srcBasename; 80 if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { 81 srcBasename = baseSrcName; 82 if (_buf[*_buflen - 1] != '/') { 83 if (++(*_buflen) > BUFSIZ) 84 name_too_long__exit(); 85 strcat(_buf, "/"); 86 } 87 } 88 if (*_buflen + strlen(srcBasename) > BUFSIZ) 89 name_too_long__exit(); 90 strcat(_buf, srcBasename); 91 return; 92 93} 94 95static int 96cp_mv_Action(const char *fileName, struct stat *statbuf, void* junk) 97{ 98 char destName[BUFSIZ + 1]; 99 size_t destLen; 100 const char *srcBasename; 101 char *name; 102 103 strcpy(destName, pBaseDestName); 104 destLen = strlen(destName); 105 106 if (srcDirFlag == TRUE) { 107 if (recursiveFlag == FALSE) { 108 error_msg(omitting_directory, baseSrcName); 109 return TRUE; 110 } 111 srcBasename = (strstr(fileName, baseSrcName) 112 + strlen(baseSrcName)); 113 114 if (destLen + strlen(srcBasename) > BUFSIZ) { 115 error_msg(name_too_long); 116 return FALSE; 117 } 118 strcat(destName, srcBasename); 119 } 120 else if (destDirFlag == TRUE) { 121 fill_baseDest_buf(&destName[0], &destLen); 122 } 123 else { 124 srcBasename = baseSrcName; 125 } 126 if (mv_Action_first_time && (dz_i == is_mv)) { 127 mv_Action_first_time = errno = 0; 128 if (rename(fileName, destName) < 0 && errno != EXDEV) { 129 perror_msg("rename(%s, %s)", fileName, destName); 130 goto do_copyFile; /* Try anyway... */ 131 } 132 else if (errno == EXDEV) 133 goto do_copyFile; 134 else 135 longjmp(catch, 1); /* succeeded with rename() */ 136 } 137 do_copyFile: 138 if (preserveFlag == TRUE && statbuf->st_nlink > 1) { 139 if (is_in_ino_dev_hashtable(statbuf, &name)) { 140 if (link(name, destName) < 0) { 141 perror_msg("link(%s, %s)", name, destName); 142 return FALSE; 143 } 144 return TRUE; 145 } 146 else { 147 add_to_ino_dev_hashtable(statbuf, destName); 148 } 149 } 150 return copy_file(fileName, destName, preserveFlag, followLinks, forceFlag); 151} 152 153static int 154rm_Action(const char *fileName, struct stat *statbuf, void* junk) 155{ 156 int status = TRUE; 157 158 if (S_ISDIR(statbuf->st_mode)) { 159 if (rmdir(fileName) < 0) { 160 perror_msg("rmdir(%s)", fileName); 161 status = FALSE; 162 } 163 } else if (unlink(fileName) < 0) { 164 perror_msg("unlink(%s)", fileName); 165 status = FALSE; 166 } 167 return status; 168} 169 170extern int cp_mv_main(int argc, char **argv) 171{ 172 volatile int i; 173 int c; 174 RESERVE_BB_BUFFER(baseDestName,BUFSIZ + 1); 175 pBaseDestName = baseDestName; /* available globally */ 176 177 if (*applet_name == 'c' && *(applet_name + 1) == 'p') 178 dz_i = is_cp; 179 else 180 dz_i = is_mv; 181 if (argc < 3) 182 show_usage(); 183 184 if (dz_i == is_cp) { 185 recursiveFlag = preserveFlag = forceFlag = FALSE; 186 followLinks = TRUE; 187 while ((c = getopt(argc, argv, "adpRf")) != EOF) { 188 switch (c) { 189 case 'a': 190 followLinks = FALSE; 191 preserveFlag = TRUE; 192 recursiveFlag = TRUE; 193 break; 194 case 'd': 195 followLinks = FALSE; 196 break; 197 case 'p': 198 preserveFlag = TRUE; 199 break; 200 case 'R': 201 recursiveFlag = TRUE; 202 break; 203 case 'f': 204 forceFlag = TRUE; 205 break; 206 default: 207 show_usage(); 208 } 209 } 210 if ((argc - optind) < 2) { 211 show_usage(); 212 } 213 } else { /* (dz_i == is_mv) */ 214 /* Initialize optind to 1, since in libc5 optind 215 * is not initialized until getopt() is called 216 * (or until sneaky programmers force it...). */ 217 optind = 1; 218 recursiveFlag = preserveFlag = TRUE; 219 followLinks = FALSE; 220 } 221 222 223 if (strlen(argv[argc - 1]) > BUFSIZ) { 224 error_msg(name_too_long); 225 goto exit_false; 226 } 227 strcpy(baseDestName, argv[argc - 1]); 228 baseDestLen = strlen(baseDestName); 229 if (baseDestLen == 0) 230 goto exit_false; 231 232 destDirFlag = is_directory(baseDestName, TRUE, &destStatBuf); 233 if (argc - optind > 2 && destDirFlag == FALSE) { 234 error_msg(not_a_directory, baseDestName); 235 goto exit_false; 236 } 237 238 for (i = optind; i < (argc-1); i++) { 239 size_t srcLen; 240 volatile int flags_memo; 241 int status; 242 243 baseSrcName=argv[i]; 244 245 if ((srcLen = strlen(baseSrcName)) > BUFSIZ) 246 name_too_long__exit(); 247 248 if (srcLen == 0) continue; /* "" */ 249 250 srcDirFlag = is_directory(baseSrcName, followLinks, &srcStatBuf); 251 252 if ((flags_memo = (recursiveFlag == TRUE && 253 srcDirFlag == TRUE && destDirFlag == TRUE))) { 254 255 struct stat sb; 256 int state = 0; 257 char *pushd, *d, *p; 258 259 if ((pushd = getcwd(NULL, BUFSIZ + 1)) == NULL) { 260 perror_msg("getcwd()"); 261 continue; 262 } 263 if (chdir(baseDestName) < 0) { 264 perror_msg("chdir(%s)", baseSrcName); 265 continue; 266 } 267 if ((d = getcwd(NULL, BUFSIZ + 1)) == NULL) { 268 perror_msg("getcwd()"); 269 continue; 270 } 271 while (!state && *d != '\0') { 272 if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */ 273 perror_msg("stat(%s)", d); 274 state = -1; 275 continue; 276 } 277 if ((sb.st_ino == srcStatBuf.st_ino) && 278 (sb.st_dev == srcStatBuf.st_dev)) { 279 error_msg("Cannot %s `%s' into a subdirectory of itself, " 280 "`%s/%s'", applet_name, baseSrcName, 281 baseDestName, baseSrcName); 282 state = -1; 283 continue; 284 } 285 if ((p = strrchr(d, '/')) != NULL) { 286 *p = '\0'; 287 } 288 } 289 if (chdir(pushd) < 0) { 290 perror_msg("chdir(%s)", pushd); 291 free(pushd); 292 free(d); 293 continue; 294 } 295 free(pushd); 296 free(d); 297 if (state < 0) 298 continue; 299 else 300 fill_baseDest_buf(baseDestName, &baseDestLen); 301 } 302 status = setjmp(catch); 303 if (status == 0) { 304 mv_Action_first_time = 1; 305 if (recursive_action(baseSrcName, 306 recursiveFlag, followLinks, FALSE, 307 cp_mv_Action, cp_mv_Action, NULL) == FALSE) goto exit_false; 308 if (dz_i == is_mv && 309 recursive_action(baseSrcName, 310 recursiveFlag, followLinks, TRUE, 311 rm_Action, rm_Action, NULL) == FALSE) goto exit_false; 312 } 313 if (flags_memo) 314 *(baseDestName + baseDestLen) = '\0'; 315 } 316 return EXIT_SUCCESS; 317 exit_false: 318 return EXIT_FAILURE; 319} 320 321/* 322Local Variables: 323c-file-style: "linux" 324c-basic-offset: 4 325tab-width: 4 326End: 327*/ 328