diff options
Diffstat (limited to 'cp_mv.c')
-rw-r--r-- | cp_mv.c | 279 |
1 files changed, 181 insertions, 98 deletions
@@ -37,11 +37,15 @@ | |||
37 | #include <utime.h> | 37 | #include <utime.h> |
38 | #include <dirent.h> | 38 | #include <dirent.h> |
39 | #include <sys/param.h> | 39 | #include <sys/param.h> |
40 | #include <setjmp.h> /* Ok to use this since `ash' does, therefore it's in the libc subset already. */ | ||
41 | #include <string.h> | ||
42 | #include <unistd.h> | ||
43 | #include <errno.h> | ||
40 | 44 | ||
41 | #define is_cp 0 | 45 | #define is_cp 0 |
42 | #define is_mv 1 | 46 | #define is_mv 1 |
47 | static int dz_i; /* index into cp_mv_usage */ | ||
43 | static const char *dz; /* dollar zero, .bss */ | 48 | static const char *dz; /* dollar zero, .bss */ |
44 | static int dz_i; /* index, .bss */ | ||
45 | static const char *cp_mv_usage[] = /* .rodata */ | 49 | static const char *cp_mv_usage[] = /* .rodata */ |
46 | { | 50 | { |
47 | "cp [OPTION]... SOURCE DEST\n" | 51 | "cp [OPTION]... SOURCE DEST\n" |
@@ -55,92 +59,131 @@ static const char *cp_mv_usage[] = /* .rodata */ | |||
55 | "mv SOURCE DEST\n" | 59 | "mv SOURCE DEST\n" |
56 | " or: mv SOURCE... DIRECTORY\n\n" | 60 | " or: mv SOURCE... DIRECTORY\n\n" |
57 | "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n" | 61 | "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n" |
58 | "Warning!! This is not GNU `mv'. It does not preserve hard links.\n" | ||
59 | }; | 62 | }; |
60 | 63 | ||
61 | extern int cp_mv_main(int argc, char **argv) | 64 | static int recursiveFlag; |
65 | static int followLinks; | ||
66 | static int preserveFlag; | ||
67 | |||
68 | static const char *baseSrcName; | ||
69 | static int srcDirFlag; | ||
70 | static struct stat srcStatBuf; | ||
71 | |||
72 | static char baseDestName[PATH_MAX + 1]; | ||
73 | static size_t baseDestLen; | ||
74 | static int destDirFlag; | ||
75 | static struct stat destStatBuf; | ||
76 | |||
77 | static jmp_buf catch; | ||
78 | static volatile int mv_Action_first_time; | ||
79 | |||
80 | static void name_too_long__exit (void) __attribute__((noreturn)); | ||
81 | |||
82 | static | ||
83 | void name_too_long__exit (void) | ||
62 | { | 84 | { |
63 | __label__ name_too_long__exit; | 85 | fprintf(stderr, name_too_long, dz); |
64 | __label__ exit_false; | 86 | exit FALSE; |
65 | 87 | } | |
66 | int recursiveFlag; | 88 | |
67 | int followLinks; | 89 | static void |
68 | int preserveFlag; | 90 | fill_baseDest_buf(char *_buf, size_t * _buflen) { |
69 | 91 | const char *srcBasename; | |
70 | const char *baseSrcName; | 92 | if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { |
71 | int srcDirFlag; | 93 | srcBasename = baseSrcName; |
72 | struct stat srcStatBuf; | 94 | if (_buf[*_buflen - 1] != '/') { |
73 | 95 | if (++(*_buflen) > PATH_MAX) | |
74 | char baseDestName[PATH_MAX + 1]; | 96 | name_too_long__exit(); |
75 | size_t baseDestLen; | 97 | strcat(_buf, "/"); |
76 | int destDirFlag; | ||
77 | struct stat destStatBuf; | ||
78 | |||
79 | void fill_baseDest_buf(char *_buf, size_t * _buflen) { | ||
80 | const char *srcBasename; | ||
81 | if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { | ||
82 | srcBasename = baseSrcName; | ||
83 | if (_buf[*_buflen - 1] != '/') { | ||
84 | if (++(*_buflen) > PATH_MAX) | ||
85 | goto name_too_long__exit; | ||
86 | strcat(_buf, "/"); | ||
87 | } | ||
88 | } | 98 | } |
89 | if (*_buflen + strlen(srcBasename) > PATH_MAX) | ||
90 | goto name_too_long__exit; | ||
91 | strcat(_buf, srcBasename); | ||
92 | return; | ||
93 | } | 99 | } |
100 | if (*_buflen + strlen(srcBasename) > PATH_MAX) | ||
101 | name_too_long__exit(); | ||
102 | strcat(_buf, srcBasename); | ||
103 | return; | ||
104 | |||
105 | } | ||
94 | 106 | ||
95 | int fileAction(const char *fileName, struct stat *statbuf) { | 107 | static int |
96 | char destName[PATH_MAX + 1]; | 108 | cp_mv_Action(const char *fileName, struct stat *statbuf) |
97 | size_t destLen; | 109 | { |
98 | const char *srcBasename; | 110 | char destName[PATH_MAX + 1]; |
111 | size_t destLen; | ||
112 | const char *srcBasename; | ||
113 | char *name; | ||
99 | 114 | ||
100 | strcpy(destName, baseDestName); | 115 | strcpy(destName, baseDestName); |
101 | destLen = strlen(destName); | 116 | destLen = strlen(destName); |
102 | 117 | ||
103 | if (srcDirFlag == TRUE) { | 118 | if (srcDirFlag == TRUE) { |
104 | if (recursiveFlag == FALSE) { | 119 | if (recursiveFlag == FALSE) { |
105 | fprintf(stderr, omitting_directory, "cp", baseSrcName); | 120 | fprintf(stderr, omitting_directory, dz, baseSrcName); |
106 | return TRUE; | 121 | return TRUE; |
107 | } | 122 | } |
108 | srcBasename = (strstr(fileName, baseSrcName) | 123 | srcBasename = (strstr(fileName, baseSrcName) |
109 | + strlen(baseSrcName)); | 124 | + strlen(baseSrcName)); |
110 | 125 | ||
111 | if (destLen + strlen(srcBasename) > PATH_MAX) { | 126 | if (destLen + strlen(srcBasename) > PATH_MAX) { |
112 | fprintf(stderr, name_too_long, "cp"); | 127 | fprintf(stderr, name_too_long, dz); |
128 | return FALSE; | ||
129 | } | ||
130 | strcat(destName, srcBasename); | ||
131 | } | ||
132 | else if (destDirFlag == TRUE) { | ||
133 | fill_baseDest_buf(&destName[0], &destLen); | ||
134 | } | ||
135 | else { | ||
136 | srcBasename = baseSrcName; | ||
137 | } | ||
138 | if (mv_Action_first_time && (dz_i == is_mv)) { | ||
139 | mv_Action_first_time = errno = 0; | ||
140 | if (rename(fileName, destName) < 0 && errno != EXDEV) { | ||
141 | fprintf(stderr, "%s: rename(%s, %s): %s\n", | ||
142 | dz, fileName, destName, strerror(errno)); | ||
143 | goto do_copyFile; /* Try anyway... */ | ||
144 | } | ||
145 | else if (errno == EXDEV) | ||
146 | goto do_copyFile; | ||
147 | else | ||
148 | longjmp(catch, 1); /* succeeded with rename() */ | ||
149 | } | ||
150 | do_copyFile: | ||
151 | if (preserveFlag == TRUE && statbuf->st_nlink > 1) { | ||
152 | if (is_in_ino_dev_hashtable(statbuf, &name)) { | ||
153 | if (link(name, destName) < 0) { | ||
154 | fprintf(stderr, "%s: link(%s, %s): %s\n", | ||
155 | dz, name, destName, strerror(errno)); | ||
113 | return FALSE; | 156 | return FALSE; |
114 | } | 157 | } |
115 | strcat(destName, srcBasename); | 158 | return TRUE; |
116 | } else if (destDirFlag == TRUE) { | ||
117 | fill_baseDest_buf(&destName[0], &destLen); | ||
118 | } else { | ||
119 | srcBasename = baseSrcName; | ||
120 | } | 159 | } |
121 | return copyFile(fileName, destName, preserveFlag, followLinks); | 160 | else { |
122 | } | 161 | add_to_ino_dev_hashtable(statbuf, destName); |
123 | |||
124 | int rmfileAction(const char *fileName, struct stat *statbuf) { | ||
125 | if (unlink(fileName) < 0) { | ||
126 | perror(fileName); | ||
127 | return FALSE; | ||
128 | } | 162 | } |
129 | return TRUE; | ||
130 | } | 163 | } |
164 | return copyFile(fileName, destName, preserveFlag, followLinks); | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | rm_Action(const char *fileName, struct stat *statbuf) | ||
169 | { | ||
170 | int status = TRUE; | ||
131 | 171 | ||
132 | int rmdirAction(const char *fileName, struct stat *statbuf) { | 172 | if (S_ISDIR(statbuf->st_mode)) { |
133 | if (rmdir(fileName) < 0) { | 173 | if (rmdir(fileName) < 0) { |
134 | perror(fileName); | 174 | fprintf(stderr, "%s: rmdir(%s): %s\n", dz, fileName, strerror(errno)); |
135 | return FALSE; | 175 | status = FALSE; |
136 | } | 176 | } |
137 | return TRUE; | 177 | } else if (unlink(fileName) < 0) { |
178 | fprintf(stderr, "%s: unlink(%s): %s\n", dz, fileName, strerror(errno)); | ||
179 | status = FALSE; | ||
138 | } | 180 | } |
181 | return status; | ||
182 | } | ||
139 | 183 | ||
140 | if ((dz = strrchr(*argv, '/')) == NULL) | 184 | extern int cp_mv_main(int argc, char **argv) |
141 | dz = *argv; | 185 | { |
142 | else | 186 | dz = *argv; /* already basename'd by busybox.c:main */ |
143 | dz++; | ||
144 | if (*dz == 'c' && *(dz + 1) == 'p') | 187 | if (*dz == 'c' && *(dz + 1) == 'p') |
145 | dz_i = is_cp; | 188 | dz_i = is_cp; |
146 | else | 189 | else |
@@ -199,53 +242,93 @@ extern int cp_mv_main(int argc, char **argv) | |||
199 | 242 | ||
200 | while (argc-- > 1) { | 243 | while (argc-- > 1) { |
201 | size_t srcLen; | 244 | size_t srcLen; |
202 | int flags_memo; | 245 | volatile int flags_memo; |
246 | int status; | ||
203 | 247 | ||
204 | baseSrcName = *(argv++); | 248 | baseSrcName = *(argv++); |
205 | 249 | ||
206 | if ((srcLen = strlen(baseSrcName)) > PATH_MAX) | 250 | if ((srcLen = strlen(baseSrcName)) > PATH_MAX) |
207 | goto name_too_long__exit; | 251 | name_too_long__exit(); |
208 | 252 | ||
209 | if (srcLen == 0) | 253 | if (srcLen == 0) continue; /* "" */ |
210 | continue; | ||
211 | 254 | ||
212 | srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf); | 255 | srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf); |
213 | 256 | ||
214 | if ((flags_memo = (recursiveFlag == TRUE && | 257 | if ((flags_memo = (recursiveFlag == TRUE && |
215 | srcDirFlag == TRUE && destDirFlag == TRUE))) { | 258 | srcDirFlag == TRUE && destDirFlag == TRUE))) { |
216 | if ((destStatBuf.st_ino == srcStatBuf.st_ino) && | 259 | |
217 | (destStatBuf.st_rdev == srcStatBuf.st_rdev)) { | 260 | struct stat sb; |
218 | fprintf(stderr, | 261 | int state = 0; |
219 | "%s: Cannot %s `%s' into a subdirectory of itself, `%s/%s'\n", | 262 | char *pushd, *d, *p; |
220 | dz, dz, baseSrcName, baseDestName, baseSrcName); | 263 | |
221 | continue; | 264 | if ((pushd = getcwd(NULL, PATH_MAX + 1)) == NULL) { |
265 | fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno)); | ||
266 | continue; | ||
267 | } | ||
268 | if (chdir(baseDestName) < 0) { | ||
269 | fprintf(stderr, "%s: chdir(%s): %s\n", dz, baseSrcName, strerror(errno)); | ||
270 | continue; | ||
271 | } | ||
272 | if ((d = getcwd(NULL, PATH_MAX + 1)) == NULL) { | ||
273 | fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno)); | ||
274 | continue; | ||
275 | } | ||
276 | while (!state && *d != '\0') { | ||
277 | if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */ | ||
278 | fprintf(stderr, "%s: stat(%s) :%s\n", dz, d, strerror(errno)); | ||
279 | state = -1; | ||
280 | continue; | ||
281 | } | ||
282 | if ((sb.st_ino == srcStatBuf.st_ino) && | ||
283 | (sb.st_dev == srcStatBuf.st_dev)) { | ||
284 | fprintf(stderr, | ||
285 | "%s: Cannot %s `%s' " | ||
286 | "into a subdirectory of itself, `%s/%s'\n", | ||
287 | dz, dz, baseSrcName, baseDestName, baseSrcName); | ||
288 | state = -1; | ||
289 | continue; | ||
222 | } | 290 | } |
291 | if ((p = strrchr(d, '/')) != NULL) { | ||
292 | *p = '\0'; | ||
293 | } | ||
294 | } | ||
295 | if (chdir(pushd) < 0) { | ||
296 | fprintf(stderr, "%s: chdir(%s): %s\n", dz, pushd, strerror(errno)); | ||
297 | free(pushd); | ||
298 | free(d); | ||
299 | continue; | ||
300 | } | ||
301 | free(pushd); | ||
302 | free(d); | ||
303 | if (state < 0) | ||
304 | continue; | ||
305 | else | ||
223 | fill_baseDest_buf(baseDestName, &baseDestLen); | 306 | fill_baseDest_buf(baseDestName, &baseDestLen); |
224 | } | 307 | } |
225 | if (recursiveAction(baseSrcName, | 308 | status = setjmp(catch); |
226 | recursiveFlag, followLinks, FALSE, | 309 | if (status == 0) { |
227 | fileAction, fileAction) == FALSE) | 310 | mv_Action_first_time = 1; |
228 | goto exit_false; | 311 | if (recursiveAction(baseSrcName, |
229 | 312 | recursiveFlag, followLinks, FALSE, | |
230 | if (dz_i == is_mv && | 313 | cp_mv_Action, cp_mv_Action) == FALSE) goto exit_false; |
231 | recursiveAction(baseSrcName, | 314 | if (dz_i == is_mv && |
232 | recursiveFlag, followLinks, TRUE, | 315 | recursiveAction(baseSrcName, |
233 | rmfileAction, rmdirAction) == FALSE) | 316 | recursiveFlag, followLinks, TRUE, |
234 | goto exit_false; | 317 | rm_Action, rm_Action) == FALSE) goto exit_false; |
235 | 318 | } | |
236 | if (flags_memo) | 319 | if (flags_memo) |
237 | *(baseDestName + baseDestLen) = '\0'; | 320 | *(baseDestName + baseDestLen) = '\0'; |
238 | } | 321 | } |
239 | 322 | // exit_true: | |
240 | exit TRUE; | 323 | exit TRUE; |
241 | 324 | exit_false: | |
242 | name_too_long__exit: | ||
243 | fprintf(stderr, name_too_long, "cp"); | ||
244 | exit_false: | ||
245 | exit FALSE; | 325 | exit FALSE; |
246 | } | 326 | } |
247 | 327 | ||
248 | // Local Variables: | 328 | /* |
249 | // c-file-style: "linux" | 329 | Local Variables: |
250 | // tab-width: 4 | 330 | c-file-style: "linux" |
251 | // End: | 331 | c-basic-offset: 4 |
332 | tab-width: 4 | ||
333 | End: | ||
334 | */ | ||