blob: b15235a2573cda3375f2dd1f5833bd457411884a [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Erik Andersenfac10d72000-02-07 05:29:42 +00002/*
3 * Mini `cp' and `mv' implementation for BusyBox.
4 *
5 *
6 * Copyright (C) 1999 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 "internal.h"
29#define BB_DECLARE_EXTERN
30#define bb_need_name_too_long
31#define bb_need_omitting_directory
32#define bb_need_not_a_directory
33#include "messages.c"
34
35#include <stdio.h>
36#include <time.h>
37#include <utime.h>
38#include <dirent.h>
39#include <sys/param.h>
Erik Andersen61677fe2000-04-13 01:18:56 +000040#include <setjmp.h>
Erik Andersen029011b2000-03-04 21:19:32 +000041#include <string.h>
42#include <unistd.h>
43#include <errno.h>
Erik Andersenfac10d72000-02-07 05:29:42 +000044
45#define is_cp 0
46#define is_mv 1
Erik Andersen029011b2000-03-04 21:19:32 +000047static int dz_i; /* index into cp_mv_usage */
Erik Andersene49d5ec2000-02-08 19:58:47 +000048static const char *dz; /* dollar zero, .bss */
Erik Andersene49d5ec2000-02-08 19:58:47 +000049static const char *cp_mv_usage[] = /* .rodata */
Erik Andersenfac10d72000-02-07 05:29:42 +000050{
Erik Andersene49d5ec2000-02-08 19:58:47 +000051 "cp [OPTION]... SOURCE DEST\n"
Erik Andersen1d1d9502000-04-21 01:26:49 +000052 " or: cp [OPTION]... SOURCE... DIRECTORY\n"
53#ifndef BB_FEATURE_TRIVIAL_HELP
54 "\nCopies SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +000055 "\n"
Erik Andersen1d1d9502000-04-21 01:26:49 +000056 "\t-a\tSame as -dpR\n"
57 "\t-d\tPreserves links\n"
58 "\t-p\tPreserves file attributes if possible\n"
Erik Andersen94f5e0b2000-05-01 19:10:52 +000059 "\t-f\tforce (implied; ignored) - always set\n"
Erik Andersen1d1d9502000-04-21 01:26:49 +000060 "\t-R\tCopies directories recursively\n"
61#endif
62 ,
Erik Andersene49d5ec2000-02-08 19:58:47 +000063 "mv SOURCE DEST\n"
Erik Andersen1d1d9502000-04-21 01:26:49 +000064 " or: mv SOURCE... DIRECTORY\n"
65#ifndef BB_FEATURE_TRIVIAL_HELP
66 "\nRename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n"
67#endif
Erik Andersenfac10d72000-02-07 05:29:42 +000068};
69
Erik Andersen029011b2000-03-04 21:19:32 +000070static int recursiveFlag;
71static int followLinks;
72static int preserveFlag;
Erik Andersen59b9e872000-05-10 05:05:45 +000073static int forceFlag;
Erik Andersen029011b2000-03-04 21:19:32 +000074
75static const char *baseSrcName;
76static int srcDirFlag;
77static struct stat srcStatBuf;
78
Erik Andersen4f3f7572000-04-28 00:18:56 +000079static char baseDestName[BUFSIZ + 1];
Erik Andersen029011b2000-03-04 21:19:32 +000080static size_t baseDestLen;
81static int destDirFlag;
82static struct stat destStatBuf;
83
84static jmp_buf catch;
85static volatile int mv_Action_first_time;
86
87static void name_too_long__exit (void) __attribute__((noreturn));
88
89static
90void name_too_long__exit (void)
91{
92 fprintf(stderr, name_too_long, dz);
Eric Andersenb6106152000-06-19 17:25:40 +000093 exit(FALSE);
Erik Andersen029011b2000-03-04 21:19:32 +000094}
95
96static void
97fill_baseDest_buf(char *_buf, size_t * _buflen) {
98 const char *srcBasename;
99 if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) {
100 srcBasename = baseSrcName;
101 if (_buf[*_buflen - 1] != '/') {
Erik Andersen4f3f7572000-04-28 00:18:56 +0000102 if (++(*_buflen) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000103 name_too_long__exit();
104 strcat(_buf, "/");
105 }
106 }
Erik Andersen4f3f7572000-04-28 00:18:56 +0000107 if (*_buflen + strlen(srcBasename) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000108 name_too_long__exit();
109 strcat(_buf, srcBasename);
110 return;
111
112}
113
114static int
Erik Andersen3364d782000-03-28 00:58:14 +0000115cp_mv_Action(const char *fileName, struct stat *statbuf, void* junk)
Erik Andersen029011b2000-03-04 21:19:32 +0000116{
Erik Andersen4f3f7572000-04-28 00:18:56 +0000117 char destName[BUFSIZ + 1];
Erik Andersen029011b2000-03-04 21:19:32 +0000118 size_t destLen;
119 const char *srcBasename;
120 char *name;
121
122 strcpy(destName, baseDestName);
123 destLen = strlen(destName);
124
125 if (srcDirFlag == TRUE) {
126 if (recursiveFlag == FALSE) {
127 fprintf(stderr, omitting_directory, dz, baseSrcName);
128 return TRUE;
129 }
130 srcBasename = (strstr(fileName, baseSrcName)
131 + strlen(baseSrcName));
132
Erik Andersen4f3f7572000-04-28 00:18:56 +0000133 if (destLen + strlen(srcBasename) > BUFSIZ) {
Erik Andersen029011b2000-03-04 21:19:32 +0000134 fprintf(stderr, name_too_long, dz);
135 return FALSE;
136 }
137 strcat(destName, srcBasename);
138 }
139 else if (destDirFlag == TRUE) {
140 fill_baseDest_buf(&destName[0], &destLen);
141 }
142 else {
143 srcBasename = baseSrcName;
144 }
145 if (mv_Action_first_time && (dz_i == is_mv)) {
146 mv_Action_first_time = errno = 0;
147 if (rename(fileName, destName) < 0 && errno != EXDEV) {
148 fprintf(stderr, "%s: rename(%s, %s): %s\n",
149 dz, fileName, destName, strerror(errno));
150 goto do_copyFile; /* Try anyway... */
151 }
152 else if (errno == EXDEV)
153 goto do_copyFile;
154 else
155 longjmp(catch, 1); /* succeeded with rename() */
156 }
157 do_copyFile:
158 if (preserveFlag == TRUE && statbuf->st_nlink > 1) {
159 if (is_in_ino_dev_hashtable(statbuf, &name)) {
160 if (link(name, destName) < 0) {
161 fprintf(stderr, "%s: link(%s, %s): %s\n",
162 dz, name, destName, strerror(errno));
163 return FALSE;
164 }
165 return TRUE;
166 }
167 else {
168 add_to_ino_dev_hashtable(statbuf, destName);
169 }
170 }
Erik Andersen59b9e872000-05-10 05:05:45 +0000171 return copyFile(fileName, destName, preserveFlag, followLinks, forceFlag);
Erik Andersen029011b2000-03-04 21:19:32 +0000172}
173
174static int
Erik Andersen3364d782000-03-28 00:58:14 +0000175rm_Action(const char *fileName, struct stat *statbuf, void* junk)
Erik Andersen029011b2000-03-04 21:19:32 +0000176{
177 int status = TRUE;
178
179 if (S_ISDIR(statbuf->st_mode)) {
180 if (rmdir(fileName) < 0) {
181 fprintf(stderr, "%s: rmdir(%s): %s\n", dz, fileName, strerror(errno));
182 status = FALSE;
183 }
184 } else if (unlink(fileName) < 0) {
185 fprintf(stderr, "%s: unlink(%s): %s\n", dz, fileName, strerror(errno));
186 status = FALSE;
187 }
188 return status;
189}
190
Erik Andersenfac10d72000-02-07 05:29:42 +0000191extern int cp_mv_main(int argc, char **argv)
192{
Erik Andersen029011b2000-03-04 21:19:32 +0000193 dz = *argv; /* already basename'd by busybox.c:main */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000194 if (*dz == 'c' && *(dz + 1) == 'p')
195 dz_i = is_cp;
196 else
197 dz_i = is_mv;
198 if (argc < 3)
199 usage(cp_mv_usage[dz_i]);
200 argc--;
201 argv++;
202
203 if (dz_i == is_cp) {
Erik Andersen59b9e872000-05-10 05:05:45 +0000204 recursiveFlag = preserveFlag = forceFlag = FALSE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000205 followLinks = TRUE;
Pavel Roskinb3d235f2000-06-08 18:06:37 +0000206 while (*argv && **argv == '-') {
207 while (*++(*argv)) {
208 switch (**argv) {
209 case 'a':
210 followLinks = FALSE;
211 preserveFlag = TRUE;
212 recursiveFlag = TRUE;
213 break;
214 case 'd':
215 followLinks = FALSE;
216 break;
217 case 'p':
218 preserveFlag = TRUE;
219 break;
220 case 'R':
221 recursiveFlag = TRUE;
222 break;
223 case 'f':
224 forceFlag = TRUE;
225 break;
226 default:
227 usage(cp_mv_usage[is_cp]);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000228 }
229 }
Pavel Roskinb3d235f2000-06-08 18:06:37 +0000230 argc--;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000231 argv++;
232 }
Pavel Roskinb3d235f2000-06-08 18:06:37 +0000233 if (argc < 2) {
234 usage(cp_mv_usage[dz_i]);
Eric Andersen815e9042000-06-06 16:15:23 +0000235 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000236 } else { /* (dz_i == is_mv) */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000237 recursiveFlag = preserveFlag = TRUE;
238 followLinks = FALSE;
239 }
Eric Andersen815e9042000-06-06 16:15:23 +0000240
Erik Andersene49d5ec2000-02-08 19:58:47 +0000241
Erik Andersen4f3f7572000-04-28 00:18:56 +0000242 if (strlen(argv[argc - 1]) > BUFSIZ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000243 fprintf(stderr, name_too_long, "cp");
244 goto exit_false;
245 }
246 strcpy(baseDestName, argv[argc - 1]);
247 baseDestLen = strlen(baseDestName);
248 if (baseDestLen == 0)
249 goto exit_false;
250
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000251 destDirFlag = isDirectory(baseDestName, TRUE, &destStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000252 if ((argc > 3) && destDirFlag == FALSE) {
253 fprintf(stderr, not_a_directory, "cp", baseDestName);
254 goto exit_false;
255 }
256
257 while (argc-- > 1) {
258 size_t srcLen;
Erik Andersen029011b2000-03-04 21:19:32 +0000259 volatile int flags_memo;
260 int status;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000261
262 baseSrcName = *(argv++);
263
Erik Andersen4f3f7572000-04-28 00:18:56 +0000264 if ((srcLen = strlen(baseSrcName)) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000265 name_too_long__exit();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000266
Erik Andersen029011b2000-03-04 21:19:32 +0000267 if (srcLen == 0) continue; /* "" */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000268
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000269 srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000270
271 if ((flags_memo = (recursiveFlag == TRUE &&
272 srcDirFlag == TRUE && destDirFlag == TRUE))) {
Erik Andersen029011b2000-03-04 21:19:32 +0000273
274 struct stat sb;
275 int state = 0;
276 char *pushd, *d, *p;
277
Erik Andersen4f3f7572000-04-28 00:18:56 +0000278 if ((pushd = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000279 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
280 continue;
281 }
282 if (chdir(baseDestName) < 0) {
283 fprintf(stderr, "%s: chdir(%s): %s\n", dz, baseSrcName, strerror(errno));
284 continue;
285 }
Erik Andersen4f3f7572000-04-28 00:18:56 +0000286 if ((d = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000287 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
288 continue;
289 }
290 while (!state && *d != '\0') {
291 if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */
292 fprintf(stderr, "%s: stat(%s) :%s\n", dz, d, strerror(errno));
293 state = -1;
294 continue;
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000295 }
Erik Andersen029011b2000-03-04 21:19:32 +0000296 if ((sb.st_ino == srcStatBuf.st_ino) &&
297 (sb.st_dev == srcStatBuf.st_dev)) {
298 fprintf(stderr,
299 "%s: Cannot %s `%s' "
300 "into a subdirectory of itself, `%s/%s'\n",
301 dz, dz, baseSrcName, baseDestName, baseSrcName);
302 state = -1;
303 continue;
304 }
305 if ((p = strrchr(d, '/')) != NULL) {
306 *p = '\0';
307 }
308 }
309 if (chdir(pushd) < 0) {
310 fprintf(stderr, "%s: chdir(%s): %s\n", dz, pushd, strerror(errno));
311 free(pushd);
312 free(d);
313 continue;
314 }
315 free(pushd);
316 free(d);
317 if (state < 0)
318 continue;
319 else
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000320 fill_baseDest_buf(baseDestName, &baseDestLen);
Erik Andersenfac10d72000-02-07 05:29:42 +0000321 }
Erik Andersen029011b2000-03-04 21:19:32 +0000322 status = setjmp(catch);
323 if (status == 0) {
324 mv_Action_first_time = 1;
325 if (recursiveAction(baseSrcName,
326 recursiveFlag, followLinks, FALSE,
Erik Andersen3364d782000-03-28 00:58:14 +0000327 cp_mv_Action, cp_mv_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000328 if (dz_i == is_mv &&
329 recursiveAction(baseSrcName,
330 recursiveFlag, followLinks, TRUE,
Erik Andersen3364d782000-03-28 00:58:14 +0000331 rm_Action, rm_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000332 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000333 if (flags_memo)
334 *(baseDestName + baseDestLen) = '\0';
Erik Andersenfac10d72000-02-07 05:29:42 +0000335 }
Eric Andersenb6106152000-06-19 17:25:40 +0000336 return( TRUE);
Erik Andersen029011b2000-03-04 21:19:32 +0000337 exit_false:
Eric Andersenb6106152000-06-19 17:25:40 +0000338 return( FALSE);
Erik Andersenfac10d72000-02-07 05:29:42 +0000339}
340
Erik Andersen029011b2000-03-04 21:19:32 +0000341/*
342Local Variables:
343c-file-style: "linux"
344c-basic-offset: 4
345tab-width: 4
346End:
347*/