blob: ae35bca19ef5bc54f29b04b29350d6943a1a5a04 [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;
73
74static const char *baseSrcName;
75static int srcDirFlag;
76static struct stat srcStatBuf;
77
Erik Andersen4f3f7572000-04-28 00:18:56 +000078static char baseDestName[BUFSIZ + 1];
Erik Andersen029011b2000-03-04 21:19:32 +000079static size_t baseDestLen;
80static int destDirFlag;
81static struct stat destStatBuf;
82
83static jmp_buf catch;
84static volatile int mv_Action_first_time;
85
86static void name_too_long__exit (void) __attribute__((noreturn));
87
88static
89void name_too_long__exit (void)
90{
91 fprintf(stderr, name_too_long, dz);
92 exit FALSE;
93}
94
95static void
96fill_baseDest_buf(char *_buf, size_t * _buflen) {
97 const char *srcBasename;
98 if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) {
99 srcBasename = baseSrcName;
100 if (_buf[*_buflen - 1] != '/') {
Erik Andersen4f3f7572000-04-28 00:18:56 +0000101 if (++(*_buflen) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000102 name_too_long__exit();
103 strcat(_buf, "/");
104 }
105 }
Erik Andersen4f3f7572000-04-28 00:18:56 +0000106 if (*_buflen + strlen(srcBasename) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000107 name_too_long__exit();
108 strcat(_buf, srcBasename);
109 return;
110
111}
112
113static int
Erik Andersen3364d782000-03-28 00:58:14 +0000114cp_mv_Action(const char *fileName, struct stat *statbuf, void* junk)
Erik Andersen029011b2000-03-04 21:19:32 +0000115{
Erik Andersen4f3f7572000-04-28 00:18:56 +0000116 char destName[BUFSIZ + 1];
Erik Andersen029011b2000-03-04 21:19:32 +0000117 size_t destLen;
118 const char *srcBasename;
119 char *name;
120
121 strcpy(destName, baseDestName);
122 destLen = strlen(destName);
123
124 if (srcDirFlag == TRUE) {
125 if (recursiveFlag == FALSE) {
126 fprintf(stderr, omitting_directory, dz, baseSrcName);
127 return TRUE;
128 }
129 srcBasename = (strstr(fileName, baseSrcName)
130 + strlen(baseSrcName));
131
Erik Andersen4f3f7572000-04-28 00:18:56 +0000132 if (destLen + strlen(srcBasename) > BUFSIZ) {
Erik Andersen029011b2000-03-04 21:19:32 +0000133 fprintf(stderr, name_too_long, dz);
134 return FALSE;
135 }
136 strcat(destName, srcBasename);
137 }
138 else if (destDirFlag == TRUE) {
139 fill_baseDest_buf(&destName[0], &destLen);
140 }
141 else {
142 srcBasename = baseSrcName;
143 }
144 if (mv_Action_first_time && (dz_i == is_mv)) {
145 mv_Action_first_time = errno = 0;
146 if (rename(fileName, destName) < 0 && errno != EXDEV) {
147 fprintf(stderr, "%s: rename(%s, %s): %s\n",
148 dz, fileName, destName, strerror(errno));
149 goto do_copyFile; /* Try anyway... */
150 }
151 else if (errno == EXDEV)
152 goto do_copyFile;
153 else
154 longjmp(catch, 1); /* succeeded with rename() */
155 }
156 do_copyFile:
157 if (preserveFlag == TRUE && statbuf->st_nlink > 1) {
158 if (is_in_ino_dev_hashtable(statbuf, &name)) {
159 if (link(name, destName) < 0) {
160 fprintf(stderr, "%s: link(%s, %s): %s\n",
161 dz, name, destName, strerror(errno));
162 return FALSE;
163 }
164 return TRUE;
165 }
166 else {
167 add_to_ino_dev_hashtable(statbuf, destName);
168 }
169 }
170 return copyFile(fileName, destName, preserveFlag, followLinks);
171}
172
173static int
Erik Andersen3364d782000-03-28 00:58:14 +0000174rm_Action(const char *fileName, struct stat *statbuf, void* junk)
Erik Andersen029011b2000-03-04 21:19:32 +0000175{
176 int status = TRUE;
177
178 if (S_ISDIR(statbuf->st_mode)) {
179 if (rmdir(fileName) < 0) {
180 fprintf(stderr, "%s: rmdir(%s): %s\n", dz, fileName, strerror(errno));
181 status = FALSE;
182 }
183 } else if (unlink(fileName) < 0) {
184 fprintf(stderr, "%s: unlink(%s): %s\n", dz, fileName, strerror(errno));
185 status = FALSE;
186 }
187 return status;
188}
189
Erik Andersenfac10d72000-02-07 05:29:42 +0000190extern int cp_mv_main(int argc, char **argv)
191{
Erik Andersen029011b2000-03-04 21:19:32 +0000192 dz = *argv; /* already basename'd by busybox.c:main */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000193 if (*dz == 'c' && *(dz + 1) == 'p')
194 dz_i = is_cp;
195 else
196 dz_i = is_mv;
197 if (argc < 3)
198 usage(cp_mv_usage[dz_i]);
199 argc--;
200 argv++;
201
202 if (dz_i == is_cp) {
203 recursiveFlag = preserveFlag = FALSE;
204 followLinks = TRUE;
205 while (**argv == '-') {
206 while (*++(*argv)) {
207 switch (**argv) {
208 case 'a':
209 followLinks = FALSE;
210 preserveFlag = TRUE;
211 recursiveFlag = TRUE;
212 break;
213 case 'd':
214 followLinks = FALSE;
215 break;
216 case 'p':
217 preserveFlag = TRUE;
218 break;
219 case 'R':
220 recursiveFlag = TRUE;
221 break;
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000222 case 'f':
223 /* for compatibility; busybox cp/mv always does force */
224 break;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000225 default:
226 usage(cp_mv_usage[is_cp]);
227 }
228 }
229 argc--;
230 argv++;
231 }
232 } else { /* (dz_i == is_mv) */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000233 recursiveFlag = preserveFlag = TRUE;
234 followLinks = FALSE;
235 }
236
Erik Andersen4f3f7572000-04-28 00:18:56 +0000237 if (strlen(argv[argc - 1]) > BUFSIZ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000238 fprintf(stderr, name_too_long, "cp");
239 goto exit_false;
240 }
241 strcpy(baseDestName, argv[argc - 1]);
242 baseDestLen = strlen(baseDestName);
243 if (baseDestLen == 0)
244 goto exit_false;
245
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000246 destDirFlag = isDirectory(baseDestName, TRUE, &destStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000247 if ((argc > 3) && destDirFlag == FALSE) {
248 fprintf(stderr, not_a_directory, "cp", baseDestName);
249 goto exit_false;
250 }
251
252 while (argc-- > 1) {
253 size_t srcLen;
Erik Andersen029011b2000-03-04 21:19:32 +0000254 volatile int flags_memo;
255 int status;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000256
257 baseSrcName = *(argv++);
258
Erik Andersen4f3f7572000-04-28 00:18:56 +0000259 if ((srcLen = strlen(baseSrcName)) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000260 name_too_long__exit();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000261
Erik Andersen029011b2000-03-04 21:19:32 +0000262 if (srcLen == 0) continue; /* "" */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000263
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000264 srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000265
266 if ((flags_memo = (recursiveFlag == TRUE &&
267 srcDirFlag == TRUE && destDirFlag == TRUE))) {
Erik Andersen029011b2000-03-04 21:19:32 +0000268
269 struct stat sb;
270 int state = 0;
271 char *pushd, *d, *p;
272
Erik Andersen4f3f7572000-04-28 00:18:56 +0000273 if ((pushd = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000274 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
275 continue;
276 }
277 if (chdir(baseDestName) < 0) {
278 fprintf(stderr, "%s: chdir(%s): %s\n", dz, baseSrcName, strerror(errno));
279 continue;
280 }
Erik Andersen4f3f7572000-04-28 00:18:56 +0000281 if ((d = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000282 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
283 continue;
284 }
285 while (!state && *d != '\0') {
286 if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */
287 fprintf(stderr, "%s: stat(%s) :%s\n", dz, d, strerror(errno));
288 state = -1;
289 continue;
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000290 }
Erik Andersen029011b2000-03-04 21:19:32 +0000291 if ((sb.st_ino == srcStatBuf.st_ino) &&
292 (sb.st_dev == srcStatBuf.st_dev)) {
293 fprintf(stderr,
294 "%s: Cannot %s `%s' "
295 "into a subdirectory of itself, `%s/%s'\n",
296 dz, dz, baseSrcName, baseDestName, baseSrcName);
297 state = -1;
298 continue;
299 }
300 if ((p = strrchr(d, '/')) != NULL) {
301 *p = '\0';
302 }
303 }
304 if (chdir(pushd) < 0) {
305 fprintf(stderr, "%s: chdir(%s): %s\n", dz, pushd, strerror(errno));
306 free(pushd);
307 free(d);
308 continue;
309 }
310 free(pushd);
311 free(d);
312 if (state < 0)
313 continue;
314 else
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000315 fill_baseDest_buf(baseDestName, &baseDestLen);
Erik Andersenfac10d72000-02-07 05:29:42 +0000316 }
Erik Andersen029011b2000-03-04 21:19:32 +0000317 status = setjmp(catch);
318 if (status == 0) {
319 mv_Action_first_time = 1;
320 if (recursiveAction(baseSrcName,
321 recursiveFlag, followLinks, FALSE,
Erik Andersen3364d782000-03-28 00:58:14 +0000322 cp_mv_Action, cp_mv_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000323 if (dz_i == is_mv &&
324 recursiveAction(baseSrcName,
325 recursiveFlag, followLinks, TRUE,
Erik Andersen3364d782000-03-28 00:58:14 +0000326 rm_Action, rm_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000327 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000328 if (flags_memo)
329 *(baseDestName + baseDestLen) = '\0';
Erik Andersenfac10d72000-02-07 05:29:42 +0000330 }
Erik Andersen029011b2000-03-04 21:19:32 +0000331// exit_true:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000332 exit TRUE;
Erik Andersen029011b2000-03-04 21:19:32 +0000333 exit_false:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000334 exit FALSE;
Erik Andersenfac10d72000-02-07 05:29:42 +0000335}
336
Erik Andersen029011b2000-03-04 21:19:32 +0000337/*
338Local Variables:
339c-file-style: "linux"
340c-basic-offset: 4
341tab-width: 4
342End:
343*/