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 @@ #include <utime.h> #include <dirent.h> #include <sys/param.h> +#include <setjmp.h> /* Ok to use this since `ash' does, therefore it's in the libc subset already. */ +#include <string.h> +#include <unistd.h> +#include <errno.h> #define is_cp 0 #define is_mv 1 +static int dz_i; /* index into cp_mv_usage */ static const char *dz; /* dollar zero, .bss */ -static int dz_i; /* index, .bss */ static const char *cp_mv_usage[] = /* .rodata */ { "cp [OPTION]... SOURCE DEST\n" @@ -55,92 +59,131 @@ static const char *cp_mv_usage[] = /* .rodata */ "mv SOURCE DEST\n" " or: mv SOURCE... DIRECTORY\n\n" "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n" - "Warning!! This is not GNU `mv'. It does not preserve hard links.\n" }; -extern int cp_mv_main(int argc, char **argv) +static int recursiveFlag; +static int followLinks; +static int preserveFlag; + +static const char *baseSrcName; +static int srcDirFlag; +static struct stat srcStatBuf; + +static char baseDestName[PATH_MAX + 1]; +static size_t baseDestLen; +static int destDirFlag; +static struct stat destStatBuf; + +static jmp_buf catch; +static volatile int mv_Action_first_time; + +static void name_too_long__exit (void) __attribute__((noreturn)); + +static +void name_too_long__exit (void) { - __label__ name_too_long__exit; - __label__ exit_false; - - int recursiveFlag; - int followLinks; - int preserveFlag; - - const char *baseSrcName; - int srcDirFlag; - struct stat srcStatBuf; - - char baseDestName[PATH_MAX + 1]; - size_t baseDestLen; - int destDirFlag; - struct stat destStatBuf; - - void fill_baseDest_buf(char *_buf, size_t * _buflen) { - const char *srcBasename; - if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { - srcBasename = baseSrcName; - if (_buf[*_buflen - 1] != '/') { - if (++(*_buflen) > PATH_MAX) - goto name_too_long__exit; - strcat(_buf, "/"); - } + fprintf(stderr, name_too_long, dz); + exit FALSE; +} + +static void +fill_baseDest_buf(char *_buf, size_t * _buflen) { + const char *srcBasename; + if ((srcBasename = strrchr(baseSrcName, '/')) == NULL) { + srcBasename = baseSrcName; + if (_buf[*_buflen - 1] != '/') { + if (++(*_buflen) > PATH_MAX) + name_too_long__exit(); + strcat(_buf, "/"); } - if (*_buflen + strlen(srcBasename) > PATH_MAX) - goto name_too_long__exit; - strcat(_buf, srcBasename); - return; } + if (*_buflen + strlen(srcBasename) > PATH_MAX) + name_too_long__exit(); + strcat(_buf, srcBasename); + return; + +} - int fileAction(const char *fileName, struct stat *statbuf) { - char destName[PATH_MAX + 1]; - size_t destLen; - const char *srcBasename; +static int +cp_mv_Action(const char *fileName, struct stat *statbuf) +{ + char destName[PATH_MAX + 1]; + size_t destLen; + const char *srcBasename; + char *name; - strcpy(destName, baseDestName); - destLen = strlen(destName); + strcpy(destName, baseDestName); + destLen = strlen(destName); - if (srcDirFlag == TRUE) { - if (recursiveFlag == FALSE) { - fprintf(stderr, omitting_directory, "cp", baseSrcName); - return TRUE; - } - srcBasename = (strstr(fileName, baseSrcName) - + strlen(baseSrcName)); + if (srcDirFlag == TRUE) { + if (recursiveFlag == FALSE) { + fprintf(stderr, omitting_directory, dz, baseSrcName); + return TRUE; + } + srcBasename = (strstr(fileName, baseSrcName) + + strlen(baseSrcName)); - if (destLen + strlen(srcBasename) > PATH_MAX) { - fprintf(stderr, name_too_long, "cp"); + if (destLen + strlen(srcBasename) > PATH_MAX) { + fprintf(stderr, name_too_long, dz); + return FALSE; + } + strcat(destName, srcBasename); + } + else if (destDirFlag == TRUE) { + fill_baseDest_buf(&destName[0], &destLen); + } + else { + srcBasename = baseSrcName; + } + if (mv_Action_first_time && (dz_i == is_mv)) { + mv_Action_first_time = errno = 0; + if (rename(fileName, destName) < 0 && errno != EXDEV) { + fprintf(stderr, "%s: rename(%s, %s): %s\n", + dz, fileName, destName, strerror(errno)); + goto do_copyFile; /* Try anyway... */ + } + else if (errno == EXDEV) + goto do_copyFile; + else + longjmp(catch, 1); /* succeeded with rename() */ + } + do_copyFile: + if (preserveFlag == TRUE && statbuf->st_nlink > 1) { + if (is_in_ino_dev_hashtable(statbuf, &name)) { + if (link(name, destName) < 0) { + fprintf(stderr, "%s: link(%s, %s): %s\n", + dz, name, destName, strerror(errno)); return FALSE; } - strcat(destName, srcBasename); - } else if (destDirFlag == TRUE) { - fill_baseDest_buf(&destName[0], &destLen); - } else { - srcBasename = baseSrcName; + return TRUE; } - return copyFile(fileName, destName, preserveFlag, followLinks); - } - - int rmfileAction(const char *fileName, struct stat *statbuf) { - if (unlink(fileName) < 0) { - perror(fileName); - return FALSE; + else { + add_to_ino_dev_hashtable(statbuf, destName); } - return TRUE; } + return copyFile(fileName, destName, preserveFlag, followLinks); +} + +static int +rm_Action(const char *fileName, struct stat *statbuf) +{ + int status = TRUE; - int rmdirAction(const char *fileName, struct stat *statbuf) { + if (S_ISDIR(statbuf->st_mode)) { if (rmdir(fileName) < 0) { - perror(fileName); - return FALSE; + fprintf(stderr, "%s: rmdir(%s): %s\n", dz, fileName, strerror(errno)); + status = FALSE; } - return TRUE; + } else if (unlink(fileName) < 0) { + fprintf(stderr, "%s: unlink(%s): %s\n", dz, fileName, strerror(errno)); + status = FALSE; } + return status; +} - if ((dz = strrchr(*argv, '/')) == NULL) - dz = *argv; - else - dz++; +extern int cp_mv_main(int argc, char **argv) +{ + dz = *argv; /* already basename'd by busybox.c:main */ if (*dz == 'c' && *(dz + 1) == 'p') dz_i = is_cp; else @@ -199,53 +242,93 @@ extern int cp_mv_main(int argc, char **argv) while (argc-- > 1) { size_t srcLen; - int flags_memo; + volatile int flags_memo; + int status; baseSrcName = *(argv++); if ((srcLen = strlen(baseSrcName)) > PATH_MAX) - goto name_too_long__exit; + name_too_long__exit(); - if (srcLen == 0) - continue; + if (srcLen == 0) continue; /* "" */ srcDirFlag = isDirectory(baseSrcName, followLinks, &srcStatBuf); if ((flags_memo = (recursiveFlag == TRUE && srcDirFlag == TRUE && destDirFlag == TRUE))) { - if ((destStatBuf.st_ino == srcStatBuf.st_ino) && - (destStatBuf.st_rdev == srcStatBuf.st_rdev)) { - fprintf(stderr, - "%s: Cannot %s `%s' into a subdirectory of itself, `%s/%s'\n", - dz, dz, baseSrcName, baseDestName, baseSrcName); - continue; + + struct stat sb; + int state = 0; + char *pushd, *d, *p; + + if ((pushd = getcwd(NULL, PATH_MAX + 1)) == NULL) { + fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno)); + continue; + } + if (chdir(baseDestName) < 0) { + fprintf(stderr, "%s: chdir(%s): %s\n", dz, baseSrcName, strerror(errno)); + continue; + } + if ((d = getcwd(NULL, PATH_MAX + 1)) == NULL) { + fprintf(stderr, "%s: getcwd(): %s\n", dz, strerror(errno)); + continue; + } + while (!state && *d != '\0') { + if (stat(d, &sb) < 0) { /* stat not lstat - always dereference targets */ + fprintf(stderr, "%s: stat(%s) :%s\n", dz, d, strerror(errno)); + state = -1; + continue; + } + if ((sb.st_ino == srcStatBuf.st_ino) && + (sb.st_dev == srcStatBuf.st_dev)) { + fprintf(stderr, + "%s: Cannot %s `%s' " + "into a subdirectory of itself, `%s/%s'\n", + dz, dz, baseSrcName, baseDestName, baseSrcName); + state = -1; + continue; } + if ((p = strrchr(d, '/')) != NULL) { + *p = '\0'; + } + } + if (chdir(pushd) < 0) { + fprintf(stderr, "%s: chdir(%s): %s\n", dz, pushd, strerror(errno)); + free(pushd); + free(d); + continue; + } + free(pushd); + free(d); + if (state < 0) + continue; + else fill_baseDest_buf(baseDestName, &baseDestLen); } - if (recursiveAction(baseSrcName, - recursiveFlag, followLinks, FALSE, - fileAction, fileAction) == FALSE) - goto exit_false; - - if (dz_i == is_mv && - recursiveAction(baseSrcName, - recursiveFlag, followLinks, TRUE, - rmfileAction, rmdirAction) == FALSE) - goto exit_false; - + status = setjmp(catch); + if (status == 0) { + mv_Action_first_time = 1; + if (recursiveAction(baseSrcName, + recursiveFlag, followLinks, FALSE, + cp_mv_Action, cp_mv_Action) == FALSE) goto exit_false; + if (dz_i == is_mv && + recursiveAction(baseSrcName, + recursiveFlag, followLinks, TRUE, + rm_Action, rm_Action) == FALSE) goto exit_false; + } if (flags_memo) *(baseDestName + baseDestLen) = '\0'; } - +// exit_true: exit TRUE; - - name_too_long__exit: - fprintf(stderr, name_too_long, "cp"); - exit_false: + exit_false: exit FALSE; } -// Local Variables: -// c-file-style: "linux" -// tab-width: 4 -// End: +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ |