int basename_main(int argc, char **argv) { size_t m, n; char *s; if (argv[1] && strcmp(argv[1], "--") == 0) { argv++; argc--; } if ((unsigned)(argc-2) >= 2) { bb_show_usage(); } /* It should strip slash: /abc/def/ -> def */ s = bb_get_last_path_component_strip(*++argv); m = strlen(s); if (*++argv) { n = strlen(*argv); if ((m > n) && (strcmp(s+m-n, *argv) == 0)) { m -= n; /*s[m] = '\0'; - redundant */ } } /* puts(s) will do, but we can do without stdio this way: */ s[m++] = '\n'; /* NB: != is correct here: */ return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; }
int rm_main(int argc UNUSED_PARAM, char **argv) { int status = 0; int flags = 0; unsigned opt; opt_complementary = "f-i:i-f"; /* -v (verbose) is ignored */ opt = getopt32(argv, "fiRrv"); argv += optind; if (opt & 1) flags |= FILEUTILS_FORCE; if (opt & 2) flags |= FILEUTILS_INTERACTIVE; if (opt & (8|4)) flags |= FILEUTILS_RECUR; if (*argv != NULL) { do { const char *base = bb_get_last_path_component_strip(*argv); if (DOT_OR_DOTDOT(base)) { bb_error_msg("cannot remove '.' or '..'"); } else if (remove_file(*argv, flags) >= 0) { continue; } status = 1; } while (*++argv); } else if (!(flags & FILEUTILS_FORCE)) { bb_show_usage(); } return status; }
/* * This routine is not the same as realpath(), which * canonicalizes the given path completely. This routine only * follows trailing symlinks until a real file is reached and * returns its name. If the path ends in a dangling link or if * the target doesn't exist, the path is returned in any case. * Intermediate symlinks in the path are not expanded -- only * those at the tail. * A malloced char* is returned, which must be freed by the caller. */ char* FAST_FUNC xmalloc_follow_symlinks(const char *path) { char *buf; char *lpc; char *linkpath; int bufsize; int looping = MAXSYMLINKS + 1; buf = xstrdup(path); goto jump_in; while (1) { linkpath = xmalloc_readlink(buf); if (!linkpath) { /* not a symlink, or doesn't exist */ if (errno == EINVAL || errno == ENOENT) return buf; goto free_buf_ret_null; } if (!--looping) { free(linkpath); free_buf_ret_null: free(buf); return NULL; } if (*linkpath != '/') { bufsize += strlen(linkpath); buf = xrealloc(buf, bufsize); lpc = bb_get_last_path_component_strip(buf); strcpy(lpc, linkpath); free(linkpath); } else { free(buf); buf = linkpath; jump_in: bufsize = strlen(buf) + 1; } } }
int cp_main(int argc, char **argv) { struct stat source_stat; struct stat dest_stat; const char *last; const char *dest; int s_flags; int d_flags; int flags; int status = 0; enum { OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), OPT_H = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), OPT_L = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), }; // Need at least two arguments // Soft- and hardlinking don't mix // -P and -d are the same (-P is POSIX, -d is GNU) // -r and -R are the same // -R (and therefore -r) switches on -d (coreutils does this) // -a = -pdR opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR"; flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPHL"); argc -= optind; argv += optind; flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */ /* Default behavior of cp is to dereference, so we don't have to do * anything special when we are given -L. * The behavior of -H is *almost* like -L, but not quite, so let's * just ignore it too for fun. if (flags & OPT_L) ... if (flags & OPT_H) ... // deref command-line params only */ #if ENABLE_SELINUX if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { selinux_or_die(); } #endif last = argv[argc - 1]; /* If there are only two arguments and... */ if (argc == 2) { s_flags = cp_mv_stat2(*argv, &source_stat, (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); if (s_flags < 0) return EXIT_FAILURE; d_flags = cp_mv_stat(last, &dest_stat); if (d_flags < 0) return EXIT_FAILURE; /* ...if neither is a directory or... */ if ( !((s_flags | d_flags) & 2) || /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */ ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) ) { /* ...do a simple copy. */ dest = last; goto DO_COPY; /* NB: argc==2 -> *++argv==last */ } } while (1) { dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); DO_COPY: if (copy_file(*argv, dest, flags) < 0) { status = 1; } if (*++argv == last) { /* possibly leaking dest... */ break; } free((void*)dest); } /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ return status; }
int makemime_main(int argc UNUSED_PARAM, char **argv) { llist_t *opt_headers = NULL, *l; const char *opt_output; #define boundary opt_output enum { OPT_c = 1 << 0, // Content-Type: OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64 OPT_o = 1 << 2, // output to OPT_C = 1 << 3, // charset OPT_N = 1 << 4, // COMPAT OPT_a = 1 << 5, // additional headers OPT_m = 1 << 6, // COMPAT OPT_j = 1 << 7, // COMPAT }; INIT_G(); // parse options opt_complementary = "a::"; opts = getopt32(argv, "c:e:o:C:N:a:m:j:", &G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL ); //argc -= optind; argv += optind; // respect -o output if (opts & OPT_o) freopen(opt_output, "w", stdout); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; // put additional headers for (l = opt_headers; l; l = l->link) puts(l->data); // make a random string -- it will delimit message parts srand(monotonic_us()); boundary = xasprintf("%d-%d-%d", rand(), rand(), rand()); // put multipart header printf( "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"%s\"\n" , boundary ); // put attachments while (*argv) { printf( "\n--%s\n" "Content-Type: %s; charset=%s\n" "Content-Disposition: inline; filename=\"%s\"\n" "Content-Transfer-Encoding: base64\n" , boundary , G.content_type , G.opt_charset , bb_get_last_path_component_strip(*argv) ); encode_base64(*argv++, (const char *)stdin, ""); } // put multipart footer printf("\n--%s--\n" "\n", boundary); return EXIT_SUCCESS; #undef boundary }
static int parse(const char *boundary, char **argv) { char *line, *s, *p; const char *type; int boundary_len = strlen(boundary); const char *delims = " ;\"\t\r\n"; const char *uniq; int ntokens; const char *tokens[32]; // 32 is enough // prepare unique string pattern uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname()); //bb_info_msg("PARSE[%s]", terminator); while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) { // seek to start of MIME section // N.B. to avoid false positives let us seek to the _last_ occurance p = NULL; s = line; while ((s=strcasestr(s, "Content-Type:")) != NULL) p = s++; if (!p) goto next; //bb_info_msg("L[%s]", p); // split to tokens // TODO: strip of comments which are of form: (comment-text) ntokens = 0; tokens[ntokens] = NULL; for (s = strtok(p, delims); s; s = strtok(NULL, delims)) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; //bb_info_msg("L[%d][%s]", ntokens, s); } tokens[ntokens] = NULL; //bb_info_msg("N[%d]", ntokens); // analyse tokens type = find_token(tokens, "Content-Type:", "text/plain"); //bb_info_msg("T[%s]", type); if (0 == strncasecmp(type, "multipart/", 10)) { if (0 == strcasecmp(type+10, "mixed")) { parse(xfind_token(tokens, "boundary="), argv); } else bb_error_msg_and_die("no support of content type '%s'", type); } else { pid_t pid = pid; int rc; FILE *fp; // fetch charset const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET); // fetch encoding const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); // compose target filename char *filename = (char *)find_token(tokens, "filename=", NULL); if (!filename) filename = xasprintf(uniq, monotonic_us()); else filename = bb_get_last_path_component_strip(xstrdup(filename)); // start external helper, if any if (opts & OPT_X) { int fd[2]; xpipe(fd); pid = vfork(); if (0 == pid) { // child reads from fd[0] xdup2(fd[0], STDIN_FILENO); close(fd[0]); close(fd[1]); xsetenv("CONTENT_TYPE", type); xsetenv("CHARSET", charset); xsetenv("ENCODING", encoding); xsetenv("FILENAME", filename); BB_EXECVP(*argv, argv); _exit(EXIT_FAILURE); } // parent dumps to fd[1] close(fd[0]); fp = fdopen(fd[1], "w"); signal(SIGPIPE, SIG_IGN); // ignore EPIPE // or create a file for dump } else { char *fname = xasprintf("%s%s", *argv, filename); fp = xfopen_for_write(fname); free(fname); } // housekeeping free(filename); // dump to fp if (0 == strcasecmp(encoding, "base64")) { decode_base64(stdin, fp); } else if (0 != strcasecmp(encoding, "7bit") && 0 != strcasecmp(encoding, "8bit")) { // quoted-printable, binary, user-defined are unsupported so far bb_error_msg_and_die("no support of encoding '%s'", encoding); } else { // N.B. we have written redundant \n. so truncate the file // The following weird 2-tacts reading technique is due to // we have to not write extra \n at the end of the file // In case of -x option we could truncate the resulting file as // fseek(fp, -1, SEEK_END); // if (ftruncate(fileno(fp), ftell(fp))) // bb_perror_msg("ftruncate"); // But in case of -X we have to be much more careful. There is // no means to truncate what we already have sent to the helper. p = xmalloc_fgets_str(stdin, "\r\n"); while (p) { if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL) break; if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fputs(p, fp); p = s; } /* while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) { if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fprintf(fp, "%s\n", s); } // N.B. we have written redundant \n. so truncate the file fseek(fp, -1, SEEK_END); if (ftruncate(fileno(fp), ftell(fp))) bb_perror_msg("ftruncate"); */ } fclose(fp); // finalize helper if (opts & OPT_X) { signal(SIGPIPE, SIG_DFL); // exit if helper exited >0 rc = wait4pid(pid); if (rc) return rc+20; } // check multipart finalized if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) { free(line); break; } } next: free(line); } //bb_info_msg("ENDPARSE[%s]", boundary); return EXIT_SUCCESS; }
int cp_main(int argc, char **argv) { struct stat source_stat; struct stat dest_stat; const char *last; const char *dest; int s_flags; int d_flags; int flags; int status; enum { OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), #if ENABLE_FEATURE_CP_LONG_OPTIONS OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), #endif }; // Need at least two arguments // Soft- and hardlinking doesn't mix // -P and -d are the same (-P is POSIX, -d is GNU) // -r and -R are the same // -R (and therefore -r) turns on -d (coreutils does this) // -a = -pdR opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR"; #if ENABLE_FEATURE_CP_LONG_OPTIONS applet_long_options = "archive\0" No_argument "a" "force\0" No_argument "f" "interactive\0" No_argument "i" "link\0" No_argument "l" "dereference\0" No_argument "L" "no-dereference\0" No_argument "P" "recursive\0" No_argument "R" "symbolic-link\0" No_argument "s" "verbose\0" No_argument "v" "parents\0" No_argument "\xff" ; #endif flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv"); /* Options of cp from GNU coreutils 6.10: * -a, --archive * -f, --force * -i, --interactive * -l, --link * -L, --dereference * -P, --no-dereference * -R, -r, --recursive * -s, --symbolic-link * -v, --verbose * -H follow command-line symbolic links in SOURCE * -d same as --no-dereference --preserve=links * -p same as --preserve=mode,ownership,timestamps * -c same as --preserve=context * --parents * use full source file name under DIRECTORY * NOT SUPPORTED IN BBOX: * --backup[=CONTROL] * make a backup of each existing destination file * -b like --backup but does not accept an argument * --copy-contents * copy contents of special files when recursive * --preserve[=ATTR_LIST] * preserve attributes (default: mode,ownership,timestamps), * if possible additional attributes: security context,links,all * --no-preserve=ATTR_LIST * --remove-destination * remove each existing destination file before attempting to open * --sparse=WHEN * control creation of sparse files * --strip-trailing-slashes * remove any trailing slashes from each SOURCE argument * -S, --suffix=SUFFIX * override the usual backup suffix * -t, --target-directory=DIRECTORY * copy all SOURCE arguments into DIRECTORY * -T, --no-target-directory * treat DEST as a normal file * -u, --update * copy only when the SOURCE file is newer than the destination * file or when the destination file is missing * -x, --one-file-system * stay on this file system * -Z, --context=CONTEXT * (SELinux) set SELinux security context of copy to CONTEXT */ argc -= optind; argv += optind; /* Reverse this bit. If there is -d, bit is not set: */ flags ^= FILEUTILS_DEREFERENCE; /* coreutils 6.9 compat: * by default, "cp" derefs symlinks (creates regular dest files), * but "cp -R" does not. We switch off deref if -r or -R (see above). * However, "cp -RL" must still deref symlinks: */ if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ flags |= FILEUTILS_DEREFERENCE; #if ENABLE_SELINUX if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { selinux_or_die(); } #endif status = EXIT_SUCCESS; last = argv[argc - 1]; /* If there are only two arguments and... */ if (argc == 2) { s_flags = cp_mv_stat2(*argv, &source_stat, (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); if (s_flags < 0) return EXIT_FAILURE; d_flags = cp_mv_stat(last, &dest_stat); if (d_flags < 0) return EXIT_FAILURE; #if ENABLE_FEATURE_CP_LONG_OPTIONS if (flags & OPT_parents) { if (!(d_flags & 2)) { bb_error_msg_and_die("with --parents, the destination must be a directory"); } } #endif /* ...if neither is a directory... */ if (!((s_flags | d_flags) & 2) /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */ || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) ) { /* Do a simple copy */ dest = last; goto DO_COPY; /* NB: argc==2 -> *++argv==last */ } } while (1) { #if ENABLE_FEATURE_CP_LONG_OPTIONS if (flags & OPT_parents) { char *dest_dup; char *dest_dir; dest = concat_path_file(last, *argv); dest_dup = xstrdup(dest); dest_dir = dirname(dest_dup); if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) { return EXIT_FAILURE; } free(dest_dup); goto DO_COPY; } #endif dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); DO_COPY: if (copy_file(*argv, dest, flags) < 0) { status = EXIT_FAILURE; } if (*++argv == last) { /* possibly leaking dest... */ break; } /* don't move up: dest may be == last and not malloced! */ free((void*)dest); } /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ return status; }
int ln_main(int argc, char **argv) { int status = EXIT_SUCCESS; int opts; char *last; char *src_name; char *src; char *suffix = (char*)"~"; struct stat statbuf; int (*link_func)(const char *, const char *); opt_complementary = "-1"; /* min one arg */ opts = getopt32(argv, "sfnbS:vT", &suffix); last = argv[argc - 1]; argv += optind; argc -= optind; if ((opts & LN_LINKFILE) && argc > 2) { bb_error_msg_and_die("-T accepts 2 args max"); } if (!argv[1]) { /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */ *--argv = last; /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE" */ last = bb_get_last_path_component_strip(xstrdup(last)); } do { src_name = NULL; src = last; if (is_directory(src, (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE ) ) { if (opts & LN_LINKFILE) { bb_error_msg_and_die("'%s' is a directory", src); } src_name = xstrdup(*argv); src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); free(src_name); src_name = src; } if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) { // coreutils: "ln dangling_symlink new_hardlink" works if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { bb_simple_perror_msg(*argv); status = EXIT_FAILURE; free(src_name); continue; } } if (opts & LN_BACKUP) { char *backup; backup = xasprintf("%s%s", src, suffix); if (rename(src, backup) < 0 && errno != ENOENT) { bb_simple_perror_msg(src); status = EXIT_FAILURE; free(backup); continue; } free(backup); /* * When the source and dest are both hard links to the same * inode, a rename may succeed even though nothing happened. * Therefore, always unlink(). */ unlink(src); } else if (opts & LN_FORCE) { unlink(src); } link_func = link; if (opts & LN_SYMLINK) { link_func = symlink; } if (opts & LN_VERBOSE) { printf("'%s' -> '%s'\n", src, *argv); } if (link_func(*argv, src) != 0) { bb_simple_perror_msg(src); status = EXIT_FAILURE; } free(src_name); } while ((++argv)[1]); return status; }
int mv_main(int argc, char **argv) { struct stat dest_stat; const char *last; const char *dest; unsigned flags; int dest_exists; int status = 0; int copy_flag = 0; #if ENABLE_FEATURE_MV_LONG_OPTIONS applet_long_options = mv_longopts; #endif /* Need at least two arguments. * If more than one of -f, -i, -n is specified , only the final one * takes effect (it unsets previous options). */ opt_complementary = "-2:f-in:i-fn:n-fi"; flags = getopt32(argv, "finv"); argc -= optind; argv += optind; last = argv[argc - 1]; if (argc == 2) { dest_exists = cp_mv_stat(last, &dest_stat); if (dest_exists < 0) { return EXIT_FAILURE; } if (!(dest_exists & 2)) { /* last is not a directory */ dest = last; goto DO_MOVE; } } do { dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); dest_exists = cp_mv_stat(dest, &dest_stat); if (dest_exists < 0) { goto RET_1; } DO_MOVE: if (dest_exists) { if (flags & OPT_NOCLOBBER) goto RET_0; if (!(flags & OPT_FORCE) && ((access(dest, W_OK) < 0 && isatty(0)) || (flags & OPT_INTERACTIVE)) ) { if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { goto RET_1; /* Ouch! fprintf failed! */ } if (!bb_ask_confirmation()) { goto RET_0; } } } if (rename(*argv, dest) < 0) { struct stat source_stat; int source_exists; if (errno != EXDEV || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 ) { bb_perror_msg("can't rename '%s'", *argv); } else { static const char fmt[] ALIGN1 = "can't overwrite %sdirectory with %sdirectory"; if (dest_exists) { if (dest_exists == 3) { if (source_exists != 3) { bb_error_msg(fmt, "", "non-"); goto RET_1; } } else { if (source_exists == 3) { bb_error_msg(fmt, "non-", ""); goto RET_1; } } if (unlink(dest) < 0) { bb_perror_msg("can't remove '%s'", dest); goto RET_1; } } /* FILEUTILS_RECUR also prevents nasties like * "read from device and write contents to dst" * instead of "create same device node" */ copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; #if ENABLE_SELINUX copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; #endif if ((copy_file(*argv, dest, copy_flag) >= 0) && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) ) { goto RET_0; } } RET_1: status = 1; } RET_0: if (flags & OPT_VERBOSE) { printf("'%s' -> '%s'\n", *argv, dest); } if (dest != last) { free((void *) dest); } } while (*++argv != last); return status; }
int ln_main(int argc, char **argv) { int status = EXIT_SUCCESS; int flag; char *last; char *src_name; char *src; char *suffix = (char*)"~"; struct stat statbuf; int (*link_func)(const char *, const char *); flag = getopt32(argv, "sfnbS:", &suffix); if (argc == optind) { bb_show_usage(); } last = argv[argc - 1]; argv += optind; if (argc == optind + 1) { *--argv = last; last = bb_get_last_path_component_strip(xstrdup(last)); } do { src_name = NULL; src = last; if (is_directory(src, (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE, NULL) ) { src_name = xstrdup(*argv); src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); free(src_name); src_name = src; } if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) { // coreutils: "ln dangling_symlink new_hardlink" works if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { bb_simple_perror_msg(*argv); status = EXIT_FAILURE; free(src_name); continue; } } if (flag & LN_BACKUP) { char *backup; backup = xasprintf("%s%s", src, suffix); if (rename(src, backup) < 0 && errno != ENOENT) { bb_simple_perror_msg(src); status = EXIT_FAILURE; free(backup); continue; } free(backup); /* * When the source and dest are both hard links to the same * inode, a rename may succeed even though nothing happened. * Therefore, always unlink(). */ unlink(src); } else if (flag & LN_FORCE) { unlink(src); } link_func = link; if (flag & LN_SYMLINK) { link_func = symlink; } if (link_func(*argv, src) != 0) { bb_simple_perror_msg(src); status = EXIT_FAILURE; } free(src_name); } while ((++argv)[1]); return status; }