blob: ac1e71b823cac1beffa3440eee18a3e19cf4db4b [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);
93 exit FALSE;
94}
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;
206 while (**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;
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000223 case 'f':
Erik Andersen59b9e872000-05-10 05:05:45 +0000224 forceFlag = TRUE;
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000225 break;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000226 default:
227 usage(cp_mv_usage[is_cp]);
228 }
229 }
230 argc--;
231 argv++;
232 }
233 } else { /* (dz_i == is_mv) */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000234 recursiveFlag = preserveFlag = TRUE;
235 followLinks = FALSE;
236 }
237
Erik Andersen4f3f7572000-04-28 00:18:56 +0000238 if (strlen(argv[argc - 1]) > BUFSIZ) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000239 fprintf(stderr, name_too_long, "cp");
240 goto exit_false;
241 }
242 strcpy(baseDestName, argv[argc - 1]);
243 baseDestLen = strlen(baseDestName);
244 if (baseDestLen == 0)
245 goto exit_false;
246
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000247 destDirFlag = isDirectory(baseDestName, TRUE, &destStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000248 if ((argc > 3) && destDirFlag == FALSE) {
249 fprintf(stderr, not_a_directory, "cp", baseDestName);
250 goto exit_false;
251 }
252
253 while (argc-- > 1) {
254 size_t srcLen;
Erik Andersen029011b2000-03-04 21:19:32 +0000255 volatile int flags_memo;
256 int status;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000257
258 baseSrcName = *(argv++);
259
Erik Andersen4f3f7572000-04-28 00:18:56 +0000260 if ((srcLen = strlen(baseSrcName)) > BUFSIZ)
Erik Andersen029011b2000-03-04 21:19:32 +0000261 name_too_long__exit();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000262
Erik Andersen029011b2000-03-04 21:19:32 +0000263 if (srcLen == 0) continue; /* "" */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000264
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000265 srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000266
267 if ((flags_memo = (recursiveFlag == TRUE &&
268 srcDirFlag == TRUE && destDirFlag == TRUE))) {
Erik Andersen029011b2000-03-04 21:19:32 +0000269
270 struct stat sb;
271 int state = 0;
272 char *pushd, *d, *p;
273
Erik Andersen4f3f7572000-04-28 00:18:56 +0000274 if ((pushd = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000275 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
276 continue;
277 }
278 if (chdir(baseDestName) < 0) {
279 fprintf(stderr, "%s: chdir(%s): %s\n", dz, baseSrcName, strerror(errno));
280 continue;
281 }
Erik Andersen4f3f7572000-04-28 00:18:56 +0000282 if ((d = getcwd(NULL, BUFSIZ + 1)) == NULL) {
Erik Andersen029011b2000-03-04 21:19:32 +0000283 fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno));
284 continue;
285 }
286 while (!state && *d != '\0') {
287 if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */
288 fprintf(stderr, "%s: stat(%s) :%s\n", dz, d, strerror(errno));
289 state = -1;
290 continue;
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000291 }
Erik Andersen029011b2000-03-04 21:19:32 +0000292 if ((sb.st_ino == srcStatBuf.st_ino) &&
293 (sb.st_dev == srcStatBuf.st_dev)) {
294 fprintf(stderr,
295 "%s: Cannot %s `%s' "
296 "into a subdirectory of itself, `%s/%s'\n",
297 dz, dz, baseSrcName, baseDestName, baseSrcName);
298 state = -1;
299 continue;
300 }
301 if ((p = strrchr(d, '/')) != NULL) {
302 *p = '\0';
303 }
304 }
305 if (chdir(pushd) < 0) {
306 fprintf(stderr, "%s: chdir(%s): %s\n", dz, pushd, strerror(errno));
307 free(pushd);
308 free(d);
309 continue;
310 }
311 free(pushd);
312 free(d);
313 if (state < 0)
314 continue;
315 else
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000316 fill_baseDest_buf(baseDestName, &baseDestLen);
Erik Andersenfac10d72000-02-07 05:29:42 +0000317 }
Erik Andersen029011b2000-03-04 21:19:32 +0000318 status = setjmp(catch);
319 if (status == 0) {
320 mv_Action_first_time = 1;
321 if (recursiveAction(baseSrcName,
322 recursiveFlag, followLinks, FALSE,
Erik Andersen3364d782000-03-28 00:58:14 +0000323 cp_mv_Action, cp_mv_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000324 if (dz_i == is_mv &&
325 recursiveAction(baseSrcName,
326 recursiveFlag, followLinks, TRUE,
Erik Andersen3364d782000-03-28 00:58:14 +0000327 rm_Action, rm_Action, NULL) == FALSE) goto exit_false;
Erik Andersen029011b2000-03-04 21:19:32 +0000328 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000329 if (flags_memo)
330 *(baseDestName + baseDestLen) = '\0';
Erik Andersenfac10d72000-02-07 05:29:42 +0000331 }
Erik Andersen029011b2000-03-04 21:19:32 +0000332// exit_true:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000333 exit TRUE;
Erik Andersen029011b2000-03-04 21:19:32 +0000334 exit_false:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000335 exit FALSE;
Erik Andersenfac10d72000-02-07 05:29:42 +0000336}
337
Erik Andersen029011b2000-03-04 21:19:32 +0000338/*
339Local Variables:
340c-file-style: "linux"
341c-basic-offset: 4
342tab-width: 4
343End:
344*/