int cat_main(int argc, char **argv) { FILE *f; int retval = EXIT_SUCCESS; bb_getopt_ulflags(argc, argv, "u"); argv += optind; if (!*argv) { *--argv = "-"; } do { if ((f = bb_wfopen_input(*argv)) != NULL) { int r = bb_copyfd_eof(fileno(f), STDOUT_FILENO); bb_fclose_nonstdin(f); if (r >= 0) { continue; } } retval = EXIT_FAILURE; } while (*++argv); return retval; }
/* Die with an error message if we can't copy an entire FILE* to stdout, * then close that file. */ void FAST_FUNC xprint_and_close_file(FILE *file) { fflush_all(); // copyfd outputs error messages for us. if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) xfunc_die(); fclose(file); }
// Die with an error message if we can't copy an entire FILE * to stdout, then // close that file. void xprint_and_close_file(FILE *file) { fflush(stdout); // copyfd outputs error messages for us. if (bb_copyfd_eof(fileno(file), 1) == -1) xfunc_die(); fclose(file); }
static int ftp_send(ftp_host_info_t *server, FILE *control_stream, const char *server_path, char *local_path) { struct stat sbuf; char buf[512]; int fd_data; int fd_local; int response; /* Connect to the data socket */ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { ftp_die("PASV", buf); } fd_data = xconnect_ftpdata(server, buf); /* get the local file */ fd_local = STDIN_FILENO; if (NOT_LONE_DASH(local_path)) { fd_local = xopen(local_path, O_RDONLY, 0666); fstat(fd_local, &sbuf); sprintf(buf, "ALLO %lu", (long unsigned int)sbuf.st_size); response = ftpcmd(buf, NULL, control_stream, buf); switch (response) { case 200: case 202: break; default: close(fd_local); ftp_die("ALLO", buf); break; } } response = ftpcmd("STOR", server_path, control_stream, buf); switch (response) { case 125: case 150: break; default: close(fd_local); ftp_die("STOR", buf); } /* transfer the file */ if (bb_copyfd_eof(fd_local, fd_data) == -1) { exit(EXIT_FAILURE); } /* close it all down */ close(fd_data); if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { ftp_die("close", buf); } ftpcmd("QUIT", NULL, control_stream, buf); return EXIT_SUCCESS; }
static void dump_file(FILE *fp, const char *filename) { int fd = open(filename, O_RDONLY); if (fd >= 0) { fputs(G.jiffy_line, fp); fflush(fp); bb_copyfd_eof(fd, fileno(fp)); close(fd); fputc('\n', fp); } }
static void motd(void) { int fd; fd = open(bb_path_motd_file, O_RDONLY); if (fd >= 0) { fflush_all(); bb_copyfd_eof(fd, STDOUT_FILENO); close(fd); } }
extern void bb_xprint_and_close_file(FILE *file) { bb_xfflush_stdout(); /* Note: Do not use STDOUT_FILENO here, as this is a lib routine * and the calling code may have reassigned stdout. */ if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) { /* bb_copyfd outputs any needed messages, so just die. */ exit(bb_default_error_retval); } /* Note: Since we're reading, don't bother checking the return value * of fclose(). The only possible failure is EINTR which * should already have been taken care of. */ fclose(file); }
int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) { struct rpm_lead lead; unsigned pos; if (argv[1]) { xmove_fd(xopen(argv[1], O_RDONLY), rpm_fd); } xread(rpm_fd, &lead, sizeof(lead)); /* Just check the magic, the rest is irrelevant */ if (lead.magic != htonl(RPM_LEAD_MAGIC)) { bb_error_msg_and_die("invalid RPM magic"); } /* Skip the signature header, align to 8 bytes */ pos = skip_header(); seek_by_jump(rpm_fd, (-(int)pos) & 7); /* Skip the main header */ skip_header(); //if (SEAMLESS_COMPRESSION) // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ // signal(SIGCHLD, check_errors_in_children); /* This works, but doesn't report uncompress errors (they happen in child) */ setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) bb_error_msg_and_die("error unpacking"); if (ENABLE_FEATURE_CLEAN_UP) { close(rpm_fd); } if (SEAMLESS_COMPRESSION) { check_errors_in_children(0); return bb_got_signal; } return EXIT_SUCCESS; }
static void finish_oldfile(void) { if (TT.tempname) { // Copy the rest of the data and replace the original with the copy. char *temp; if (TT.filein != -1) { bb_copyfd_eof(TT.filein, TT.fileout); xclose(TT.filein); } xclose(TT.fileout); temp = xstrdup(TT.tempname); temp[strlen(temp) - 6] = '\0'; rename(TT.tempname, temp); free(temp); free(TT.tempname); TT.tempname = NULL; } TT.fileout = TT.filein = -1; }
int rpm2cpio_main(int argc UNUSED_PARAM, char **argv) { const char *str; int rpm_fd; INIT_G(); G.pagesize = getpagesize(); rpm_fd = rpm_gettags(argv[1]); //if (SEAMLESS_COMPRESSION) - we do this at the end instead. // /* We need to know whether child (gzip/bzip/etc) exits abnormally */ // signal(SIGCHLD, check_errors_in_children); if (ENABLE_FEATURE_SEAMLESS_LZMA && (str = rpm_getstr0(TAG_PAYLOADCOMPRESSOR)) != NULL && strcmp(str, "lzma") == 0 ) { // lzma compression can't be detected // set up decompressor without detection setup_lzma_on_fd(rpm_fd); } else { setup_unzip_on_fd(rpm_fd, /*fail_if_not_compressed:*/ 1); } if (bb_copyfd_eof(rpm_fd, STDOUT_FILENO) < 0) bb_error_msg_and_die("error unpacking"); if (ENABLE_FEATURE_CLEAN_UP) { close(rpm_fd); } if (SEAMLESS_COMPRESSION) { check_errors_in_children(0); return bb_got_signal; } return EXIT_SUCCESS; }
int bb_cat(char **argv) { int fd; int retval = EXIT_SUCCESS; if (!*argv) argv = (char**) &bb_argv_dash; do { fd = open_or_warn_stdin(*argv); if (fd >= 0) { /* This is not a xfunc - never exits */ off_t r = bb_copyfd_eof(fd, STDOUT_FILENO); if (fd != STDIN_FILENO) close(fd); if (r >= 0) continue; } retval = EXIT_FAILURE; } while (*++argv); return retval; }
static int ftp_send(ftp_host_info_t *server, FILE *control_stream, const char *server_path, char *local_path) { struct stat sbuf; char buf[512]; int fd_data; int fd_local; int response; /* Connect to the data socket */ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { bb_error_msg_and_die("PASV error: %s", buf + 4); } fd_data = xconnect_ftpdata(server, buf); if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) { bb_error_msg_and_die("CWD error: %s", buf + 4); } /* get the local file */ if ((local_path[0] == '-') && (local_path[1] == '\0')) { fd_local = STDIN_FILENO; } else { fd_local = bb_xopen(local_path, O_RDONLY); fstat(fd_local, &sbuf); sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size); response = ftpcmd(buf, NULL, control_stream, buf); switch (response) { case 200: case 202: break; default: close(fd_local); bb_error_msg_and_die("ALLO error: %s", buf + 4); break; } } response = ftpcmd("STOR ", local_path, control_stream, buf); switch (response) { case 125: case 150: break; default: close(fd_local); bb_error_msg_and_die("STOR error: %s", buf + 4); } /* transfer the file */ if (bb_copyfd_eof(fd_local, fd_data) == -1) { exit(EXIT_FAILURE); } /* close it all down */ close(fd_data); if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { bb_error_msg_and_die("error: %s", buf + 4); } ftpcmd("QUIT", NULL, control_stream, buf); return(EXIT_SUCCESS); }
int crontab_main(int argc UNUSED_PARAM, char **argv) { const struct passwd *pas; const char *crontab_dir = CRONTABS; char *tmp_fname; char *new_fname; char *user_name; /* -u USER */ int fd; int src_fd; int opt_ler; /* file [opts] Replace crontab from file * - [opts] Replace crontab from stdin * -u user User * -c dir Crontab directory * -l List crontab for user * -e Edit crontab for user * -r Delete crontab for user * bbox also supports -d == -r, but most other crontab * implementations do not. Deprecated. */ enum { OPT_u = (1 << 0), OPT_c = (1 << 1), OPT_l = (1 << 2), OPT_e = (1 << 3), OPT_r = (1 << 4), OPT_ler = OPT_l + OPT_e + OPT_r, }; opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); argv += optind; if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ /* run by non-root? */ if (opt_ler & (OPT_u|OPT_c)) bb_error_msg_and_die("only root can use -c or -u"); } if (opt_ler & OPT_u) { pas = getpwnam(user_name); if (!pas) bb_error_msg_and_die("user %s is not known", user_name); } else { /* XXX: xgetpwuid */ uid_t my_uid = getuid(); pas = getpwuid(my_uid); if (!pas) bb_perror_msg_and_die("unknown uid %d", (int)my_uid); } #define user_name DONT_USE_ME_BEYOND_THIS_POINT /* From now on, keep only -l, -e, -r bits */ opt_ler &= OPT_ler; if ((opt_ler - 1) & opt_ler) /* more than one bit set? */ bb_show_usage(); /* Read replacement file under user's UID/GID/group vector */ src_fd = STDIN_FILENO; if (!opt_ler) { /* Replace? */ if (!argv[0]) bb_show_usage(); if (NOT_LONE_DASH(argv[0])) { src_fd = open_as_user(pas, argv[0]); if (src_fd < 0) bb_error_msg_and_die("user %s cannot read %s", pas->pw_name, argv[0]); } } /* cd to our crontab directory */ xchdir(crontab_dir); tmp_fname = NULL; /* Handle requested operation */ switch (opt_ler) { default: /* case OPT_r: Delete */ unlink(pas->pw_name); break; case OPT_l: /* List */ { char *args[2] = { pas->pw_name, NULL }; return bb_cat(args); /* list exits, * the rest go play with cron update file */ } case OPT_e: /* Edit */ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid()); /* No O_EXCL: we don't want to be stuck if earlier crontabs * were killed, leaving stale temp file behind */ src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); fchown(src_fd, pas->pw_uid, pas->pw_gid); fd = open(pas->pw_name, O_RDONLY); if (fd >= 0) { bb_copyfd_eof(fd, src_fd); close(fd); xlseek(src_fd, 0, SEEK_SET); } close_on_exec_on(src_fd); /* don't want editor to see this fd */ edit_file(pas, tmp_fname); /* fall through */ case 0: /* Replace (no -l, -e, or -r were given) */ new_fname = xasprintf("%s.new", pas->pw_name); fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600); if (fd >= 0) { bb_copyfd_eof(src_fd, fd); close(fd); xrename(new_fname, pas->pw_name); } else { bb_error_msg("cannot create %s/%s", crontab_dir, new_fname); } if (tmp_fname) unlink(tmp_fname); /*free(tmp_fname);*/ /*free(new_fname);*/ } /* switch */ /* Bump notification file. Handle window where crond picks file up * before we can write our entry out. */ while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) { struct stat st; fdprintf(fd, "%s\n", pas->pw_name); if (fstat(fd, &st) != 0 || st.st_nlink != 0) { /*close(fd);*/ break; } /* st.st_nlink == 0: * file was deleted, maybe crond missed our notification */ close(fd); /* loop */ } if (fd < 0) { bb_error_msg("cannot append to %s/%s", crontab_dir, CRONUPDATE); } return 0; }
static int writeFileToTarball(const char *fileName, struct stat *statbuf, void *userData) { struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; const char *header_name; /* ** Check to see if we are dealing with a hard link. ** If so - ** Treat the first occurance of a given dev/inode as a file while ** treating any additional occurances as hard links. This is done ** by adding the file information to the HardLinkInfo linked list. */ tbInfo->hlInfo = NULL; if (statbuf->st_nlink > 1) { tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf); if (tbInfo->hlInfo == NULL) addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName); } /* It is against the rules to archive a socket */ if (S_ISSOCK(statbuf->st_mode)) { bb_error_msg("%s: socket ignored", fileName); return (TRUE); } /* It is a bad idea to store the archive we are in the process of creating, * so check the device and inode to be sure that this particular file isn't * the new tarball */ if (tbInfo->statBuf.st_dev == statbuf->st_dev && tbInfo->statBuf.st_ino == statbuf->st_ino) { bb_error_msg("%s: file is the archive; skipping", fileName); return (TRUE); } header_name = fileName; while (header_name[0] == '/') { static int alreadyWarned = FALSE; if (alreadyWarned == FALSE) { bb_error_msg("Removing leading '/' from member names"); alreadyWarned = TRUE; } header_name++; } if (strlen(fileName) >= NAME_SIZE) { bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE); return (TRUE); } if (header_name[0] == '\0') return TRUE; # ifdef CONFIG_FEATURE_TAR_FROM if (exclude_file(tbInfo->excludeList, header_name)) { return SKIP; } # endif /* CONFIG_FEATURE_TAR_FROM */ if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { return (FALSE); } /* Now, if the file is a regular file, copy it out to the tarball */ if ((tbInfo->hlInfo == NULL) && (S_ISREG(statbuf->st_mode))) { int inputFileFd; ssize_t readSize = 0; /* open the file we want to archive, and make sure all is well */ if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { bb_perror_msg("%s: Cannot open", fileName); return (FALSE); } /* write the file to the archive */ readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd); /* Pad the file up to the tar block size */ for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++) { write(tbInfo->tarFd, "\0", 1); } close(inputFileFd); } return (TRUE); }
/* Return: * -1 error, copy not made * 0 copy is made or user answered "no" in interactive mode * (failures to preserve mode/owner/times are not reported in exit code) */ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; smallint retval = 0; smallint dest_exists = 0; smallint ovr; /* Inverse of cp -d ("cp without -d") */ #define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0)) if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { /* This may be a dangling symlink. * Making [sym]links to dangling symlinks works, so... */ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) goto make_links; bb_perror_msg("can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { bb_error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } #if ENABLE_SELINUX if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) { security_context_t con; if (lgetfilecon(source, &con) >= 0) { if (setfscreatecon(con) < 0) { bb_perror_msg("can't set setfscreatecon %s", con); freecon(con); return -1; } } else if (errno == ENOTSUP || errno == ENODATA) { setfscreatecon_or_die(NULL); } else { bb_perror_msg("can't lgetfilecon %s", source); return -1; } } #endif if (S_ISDIR(source_stat.st_mode)) { DIR *dp; const char *tp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("omitting directory '%s'", source); return -1; } /* Did we ever create source ourself before? */ tp = is_in_ino_dev_hashtable(&source_stat); if (tp) { /* We did! it's a recursion! man the lifeboats... */ bb_error_msg("recursion detected, omitting directory '%s'", source); return -1; } if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("can't create directory '%s'", dest); return -1; } umask(saved_umask); /* need stat info for add_to_ino_dev_hashtable */ if (lstat(dest, &dest_stat) < 0) { bb_perror_msg("can't stat '%s'", dest); return -1; } } /* remember (dev,inode) of each created dir. * NULL: name is not remembered */ add_to_ino_dev_hashtable(&dest_stat, NULL); /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto preserve_mode_ugid_time; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if (new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 ) { bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); /* retval = -1; - WRONG! copy *WAS* made */ } goto preserve_mode_ugid_time; } if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { int (*lf)(const char *oldpath, const char *newpath); make_links: /* Hmm... maybe * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? * (but realpath returns NULL on dangling symlinks...) */ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; if (lf(source, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (lf(source, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } /* _Not_ jumping to preserve_mode_ugid_time: * (sym)links don't have those */ return 0; } if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */ !(flags & FILEUTILS_RECUR) /* "cp [-opts] regular_file thing2" */ || S_ISREG(source_stat.st_mode) /* DEREF uses stat, which never returns S_ISLNK() == true. * So the below is never true: */ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ ) { int src_fd; int dst_fd; mode_t new_mode; if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) { /* "cp -d symlink dst": create a link */ goto dont_cat; } if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { const char *link_target; link_target = is_in_ino_dev_hashtable(&source_stat); if (link_target) { if (link(link_target, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (link(link_target, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } return 0; } add_to_ino_dev_hashtable(&source_stat, dest); } src_fd = open_or_warn(source, O_RDONLY); if (src_fd < 0) return -1; /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; if (!S_ISREG(source_stat.st_mode)) new_mode = 0666; // POSIX way is a security problem versus (sym)link attacks if (!ENABLE_FEATURE_NON_POSIX_CP) { dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); } else { /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); } if (dst_fd == -1) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) { close(src_fd); return ovr; } /* It shouldn't exist. If it exists, do not open (symlink attack?) */ dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } } #if ENABLE_SELINUX if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) && is_selinux_enabled() > 0 ) { security_context_t con; if (getfscreatecon(&con) == -1) { bb_perror_msg("getfscreatecon"); return -1; } if (con) { if (setfilecon(dest, con) == -1) { bb_perror_msg("setfilecon:%s,%s", dest, con); freecon(con); return -1; } freecon(con); } } #endif if (bb_copyfd_eof(src_fd, dst_fd) == -1) retval = -1; /* Careful with writing... */ if (close(dst_fd) < 0) { bb_perror_msg("error writing to '%s'", dest); retval = -1; } /* ...but read size is already checked by bb_copyfd_eof */ close(src_fd); /* "cp /dev/something new_file" should not * copy mode of /dev/something */ if (!S_ISREG(source_stat.st_mode)) return retval; goto preserve_mode_ugid_time; } dont_cat: /* Source is a symlink or a special file */ /* We are lazy here, a bit lax with races... */ if (dest_exists) { errno = EEXIST; ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; } if (S_ISLNK(source_stat.st_mode)) { char *lpath = xmalloc_readlink_or_warn(source); if (lpath) { int r = symlink(lpath, dest); free(lpath); if (r < 0) { bb_perror_msg("can't create symlink '%s'", dest); return -1; } if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } /* _Not_ jumping to preserve_mode_ugid_time: * symlinks don't have those */ return 0; } if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ) { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("can't create '%s'", dest); return -1; } } else { bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode); return -1; } preserve_mode_ugid_time: if (flags & FILEUTILS_PRESERVE_STATUS /* Cannot happen: */ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ ) { struct timeval times[2]; times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime; times[1].tv_usec = times[0].tv_usec = 0; /* BTW, utimes sets usec-precision time - just FYI */ if (utimes(dest, times) < 0) bb_perror_msg("can't preserve %s of '%s'", "times", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } #if ENABLE_XATTR /* Preserve extended attributes. We must copy it after chown() * because it resets capabilities. */ if (copy_file_attr(source, dest) == -1) bb_perror_msg("can't preserve %s of '%s'", "extended attributes", dest); #endif if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); } return retval; }
int copy_file(const char *source, const char *dest, int flags) { struct stat source_stat; struct stat dest_stat; int dest_exists = 0; int status = 0; printf("source:%s dest:%s\n", source, dest); printf("lstat %d\t\t\tstat %d\n", lstat(source, &source_stat), stat(source, &source_stat)); printf("DIR %d\t\t\tREG %d\n", S_ISDIR(source_stat.st_mode), S_ISREG(source_stat.st_mode)); if ((!(flags & FILEUTILS_DEREFERENCE) && lstat(source, &source_stat) < 0) || ((flags & FILEUTILS_DEREFERENCE) && stat(source, &source_stat) < 0)) { return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { printf("unable to stat `%s'\n", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino) { printf("`%s' and `%s' are the same file\n", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { printf("step in folder \n\n"); DIR *dp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { printf("%s: omitting directory\n", source); return -1; } /* Create DEST. */ if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { printf("`%s' is not a directory\n", dest); return -1; } } else { mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); printf("cannot create directory `%s'\n", dest); return -1; } umask(saved_umask); } /* Recursively copy files in SOURCE. */ if ((dp = opendir(source)) == NULL) { printf("unable to open directory `%s'\n", source); status = -1; goto end; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if(new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags) < 0) status = -1; free(new_source); free(new_dest); } /* closedir have only EBADF error, but "dp" not changes */ closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { printf("unable to change permissions of `%s'\n", dest); status = -1; } } else if (S_ISREG(source_stat.st_mode)) { printf("step in file \n\n"); int src_fd; int dst_fd; src_fd = open(source, O_RDONLY); if (src_fd == -1) { printf("unable to open `%s'\n", source); return(-1); } if (dest_exists) { if (flags & FILEUTILS_INTERACTIVE) { printf("overwrite `%s'? \n", dest); } dst_fd = open(dest, O_WRONLY|O_TRUNC); if (dst_fd == -1) { if (!(flags & FILEUTILS_FORCE)) { printf("unable to open `%s'\n", dest); close(src_fd); return -1; } if (unlink(dest) < 0) { printf("unable to remove `%s'\n", dest); close(src_fd); return -1; } dest_exists = 0; } } if (!dest_exists) { dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode); if (dst_fd == -1) { printf("unable to open `%s'", dest); close(src_fd); return(-1); } } if (bb_copyfd_eof(src_fd, dst_fd) == -1) { status = -1; } if (close(dst_fd) < 0) { printf("unable to close `%s'", dest); status = -1; } if (close(src_fd) < 0) { printf("unable to close `%s'", source); status = -1; } } end: if (flags & FILEUTILS_PRESERVE_STATUS) { struct utimbuf times; times.actime = source_stat.st_atime; times.modtime = source_stat.st_mtime; if (utime(dest, ×) < 0) printf("unable to preserve times of `%s'\n", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); printf("unable to preserve ownership of `%s'\n", dest); } if (chmod(dest, source_stat.st_mode) < 0) printf("unable to preserve permissions of `%s'\n", dest); } return status; }
static int ftp_receive(ftp_host_info_t *server, FILE *control_stream, const char *local_path, char *server_path) { char buf[512]; /* I think 'filesize' usage here is bogus. Let's see... */ //off_t filesize = -1; #define filesize ((off_t)-1) int fd_data; int fd_local = -1; off_t beg_range = 0; /* Connect to the data socket */ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { ftp_die("PASV", buf); } fd_data = xconnect_ftpdata(server, buf); if (ftpcmd("SIZE", server_path, control_stream, buf) == 213) { //filesize = BB_STRTOOFF(buf + 4, NULL, 10); //if (errno || filesize < 0) // ftp_die("SIZE", buf); } else { do_continue = 0; } if (LONE_DASH(local_path)) { fd_local = STDOUT_FILENO; do_continue = 0; } if (do_continue) { struct stat sbuf; if (lstat(local_path, &sbuf) < 0) { //bb_error_msg_and_die("lstat"); printf("lstat error."); exit - 1; } if (sbuf.st_size > 0) { beg_range = sbuf.st_size; } else { do_continue = 0; } } if (do_continue) { sprintf(buf, "REST %""1""d", (int)beg_range); if (ftpcmd(buf, NULL, control_stream, buf) != 350) { do_continue = 0; } else { //if (filesize != -1) // filesize -= beg_range; } } if (ftpcmd("RETR", server_path, control_stream, buf) > 150) { ftp_die("RETR", buf); } /* only make a local file if we know that one exists on the remote server */ if (fd_local == -1) { if (do_continue) { fd_local = xopen(local_path, O_APPEND | O_WRONLY, 0666); } else { fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY, 0666); } } /* Copy the file */ if (filesize != -1) { if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) return EXIT_FAILURE; } else { if (bb_copyfd_eof(fd_data, fd_local) == -1) return EXIT_FAILURE; } /* close it all down */ close(fd_data); if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { ftp_die(NULL, buf); } ftpcmd("QUIT", NULL, control_stream, buf); return EXIT_SUCCESS; }
int lpqr_main(int argc UNUSED_PARAM, char *argv[]) { enum { OPT_P = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515" OPT_U = 1 << 1, // -U username LPR_V = 1 << 2, // -V: be verbose LPR_h = 1 << 3, // -h: want banner printed LPR_C = 1 << 4, // -C class: job "class" (? supposedly printed on banner) LPR_J = 1 << 5, // -J title: the job title for the banner page LPR_m = 1 << 6, // -m: send mail back to user LPQ_SHORT_FMT = 1 << 2, // -s: short listing format LPQ_DELETE = 1 << 3, // -d: delete job(s) LPQ_FORCE = 1 << 4, // -f: force waiting job(s) to be printed }; char tempfile[sizeof("/tmp/lprXXXXXX")]; const char *job_title; const char *printer_class = ""; // printer class, max 32 char const char *queue; // name of printer queue const char *server = "localhost"; // server[:port] of printer queue char *hostname; // N.B. IMHO getenv("USER") can be way easily spoofed! const char *user = xuid2uname(getuid()); unsigned job; unsigned opts; int fd; queue = getenv("PRINTER"); if (!queue) queue = "lp"; // parse options // TODO: set opt_complementary: s,d,f are mutually exclusive opts = getopt32(argv, (/*lp*/'r' == applet_name[2]) ? "P:U:VhC:J:m" : "P:U:sdf" , &queue, &user , &printer_class, &job_title ); argv += optind; { // queue name is to the left of '@' char *s = strchr(queue, '@'); if (s) { // server name is to the right of '@' *s = '\0'; server = s + 1; } } // do connect fd = create_and_connect_stream_or_die(server, 515); // // LPQ ------------------------ // if (/*lp*/'q' == applet_name[2]) { char cmd; // force printing of every job still in queue if (opts & LPQ_FORCE) { cmd = 1; goto command; // delete job(s) } else if (opts & LPQ_DELETE) { fdprintf(fd, "\x5" "%s %s", queue, user); while (*argv) { fdprintf(fd, " %s", *argv++); } bb_putchar('\n'); // dump current jobs status // N.B. periodical polling should be achieved // via "watch -n delay lpq" // They say it's the UNIX-way :) } else { cmd = (opts & LPQ_SHORT_FMT) ? 3 : 4; command: fdprintf(fd, "%c" "%s\n", cmd, queue); bb_copyfd_eof(fd, STDOUT_FILENO); } return EXIT_SUCCESS; } // // LPR ------------------------ // if (opts & LPR_V) bb_error_msg("connected to server"); job = getpid() % 1000; hostname = safe_gethostname(); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; fdprintf(fd, "\x2" "%s\n", queue); get_response_or_say_and_die(fd, "setting queue"); // process files do { unsigned cflen; int dfd; struct stat st; char *c; char *remote_filename; char *controlfile; // if data file is stdin, we need to dump it first if (LONE_DASH(*argv)) { strcpy(tempfile, "/tmp/lprXXXXXX"); dfd = xmkstemp(tempfile); bb_copyfd_eof(STDIN_FILENO, dfd); xlseek(dfd, 0, SEEK_SET); *argv = (char*)bb_msg_standard_input; } else { dfd = xopen(*argv, O_RDONLY); } st.st_size = 0; /* paranoia: fstat may theoretically fail */ fstat(dfd, &st); /* Apparently, some servers are buggy and won't accept 0-sized jobs. * Standard lpr works around it by refusing to send such jobs: */ if (st.st_size == 0) { bb_error_msg("nothing to print"); continue; } /* "The name ... should start with ASCII "cfA", * followed by a three digit job number, followed * by the host name which has constructed the file." * We supply 'c' or 'd' as needed for control/data file. */ remote_filename = xasprintf("fA%03u%s", job, hostname); // create control file // TODO: all lines but 2 last are constants! How we can use this fact? controlfile = xasprintf( "H" "%.32s\n" "P" "%.32s\n" /* H HOST, P USER */ "C" "%.32s\n" /* C CLASS - printed on banner page (if L cmd is also given) */ "J" "%.99s\n" /* J JOBNAME */ /* "class name for banner page and job name * for banner page commands must precede L command" */ "L" "%.32s\n" /* L USER - print banner page, with given user's name */ "M" "%.32s\n" /* M WHOM_TO_MAIL */ "l" "d%.31s\n" /* l DATA_FILE_NAME ("dfAxxx") */ , hostname, user , printer_class /* can be "" */ , ((opts & LPR_J) ? job_title : *argv) , (opts & LPR_h) ? user : "" , (opts & LPR_m) ? user : "" , remote_filename ); // delete possible "\nX\n" (that is, one-char) patterns c = controlfile; while ((c = strchr(c, '\n')) != NULL) { if (c[1] && c[2] == '\n') { overlapping_strcpy(c, c+2); } else { c++; } } // send control file if (opts & LPR_V) bb_error_msg("sending control file"); /* "Acknowledgement processing must occur as usual * after the command is sent." */ cflen = (unsigned)strlen(controlfile); fdprintf(fd, "\x2" "%u c%s\n", cflen, remote_filename); get_response_or_say_and_die(fd, "sending control file"); /* "Once all of the contents have * been delivered, an octet of zero bits is sent as * an indication that the file being sent is complete. * A second level of acknowledgement processing * must occur at this point." */ full_write(fd, controlfile, cflen + 1); /* writes NUL byte too */ get_response_or_say_and_die(fd, "sending control file"); // send data file, with name "dfaXXX" if (opts & LPR_V) bb_error_msg("sending data file"); fdprintf(fd, "\x3" "%"FILESIZE_FMT"u d%s\n", st.st_size, remote_filename); get_response_or_say_and_die(fd, "sending data file"); if (bb_copyfd_size(dfd, fd, st.st_size) != st.st_size) { // We're screwed. We sent less bytes than we advertised. bb_error_msg_and_die("local file changed size?!"); } write(fd, "", 1); // send ACK get_response_or_say_and_die(fd, "sending data file"); // delete temporary file if we dumped stdin if (*argv == (char*)bb_msg_standard_input) unlink(tempfile); // cleanup close(fd); free(remote_filename); free(controlfile); // say job accepted if (opts & LPR_V) bb_error_msg("job accepted"); // next, please! job = (job + 1) % 1000; } while (*++argv); return EXIT_SUCCESS; }
int copy_file(const char *source, const char *dest, int flags) { struct stat source_stat; struct stat dest_stat; int dest_exists = 0; int status = 0; if ((!(flags & FILEUTILS_DEREFERENCE) && lstat(source, &source_stat) < 0) || ((flags & FILEUTILS_DEREFERENCE) && stat(source, &source_stat) < 0)) { bb_perror_msg("%s", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("unable to stat `%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino) { bb_error_msg("`%s' and `%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("%s: omitting directory", source); return -1; } /* Create DEST. */ if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("`%s' is not a directory", dest); return -1; } } else { mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("cannot create directory `%s'", dest); return -1; } umask(saved_umask); } /* Recursively copy files in SOURCE. */ if ((dp = opendir(source)) == NULL) { bb_perror_msg("unable to open directory `%s'", source); status = -1; goto end; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if(new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags) < 0) status = -1; free(new_source); free(new_dest); } /* closedir have only EBADF error, but "dp" not changes */ closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0) { bb_perror_msg("unable to change permissions of `%s'", dest); status = -1; } } else if (S_ISREG(source_stat.st_mode)) { int src_fd; int dst_fd = -1; #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS char *link_name; if (!(flags & FILEUTILS_DEREFERENCE) && is_in_ino_dev_hashtable(&source_stat, &link_name)) { if (link(link_name, dest) < 0) { bb_perror_msg("unable to link `%s'", dest); return -1; } return 0; } #endif src_fd = open(source, O_RDONLY); if (src_fd == -1) { bb_perror_msg("unable to open `%s'", source); return(-1); } if (dest_exists) { if (flags & FILEUTILS_INTERACTIVE) { fprintf(stderr, "%s: overwrite `%s'? ", bb_applet_name, dest); if (!bb_ask_confirmation()) { close (src_fd); return 0; } } dst_fd = open(dest, O_WRONLY|O_TRUNC); if (dst_fd == -1) { if (!(flags & FILEUTILS_FORCE)) { bb_perror_msg("unable to open `%s'", dest); close(src_fd); return -1; } if (unlink(dest) < 0) { bb_perror_msg("unable to remove `%s'", dest); close(src_fd); return -1; } dest_exists = 0; } } if (!dest_exists) { dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode); if (dst_fd == -1) { bb_perror_msg("unable to open `%s'", dest); close(src_fd); return(-1); } } if (bb_copyfd_eof(src_fd, dst_fd) == -1) status = -1; if (close(dst_fd) < 0) { bb_perror_msg("unable to close `%s'", dest); status = -1; } if (close(src_fd) < 0) { bb_perror_msg("unable to close `%s'", source); status = -1; } } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) || S_ISLNK(source_stat.st_mode)) { if (dest_exists) { if((flags & FILEUTILS_FORCE) == 0) { fprintf(stderr, "`%s' exists\n", dest); return -1; } if(unlink(dest) < 0) { bb_perror_msg("unable to remove `%s'", dest); return -1; } } } else { bb_error_msg("internal error: unrecognized file type"); return -1; } if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode)) { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("unable to create `%s'", dest); return -1; } } else if (S_ISFIFO(source_stat.st_mode)) { if (mkfifo(dest, source_stat.st_mode) < 0) { bb_perror_msg("cannot create fifo `%s'", dest); return -1; } } else if (S_ISLNK(source_stat.st_mode)) { char *lpath; lpath = xreadlink(source); if (symlink(lpath, dest) < 0) { bb_perror_msg("cannot create symlink `%s'", dest); return -1; } free(lpath); #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("unable to preserve ownership of `%s'", dest); #endif #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS add_to_ino_dev_hashtable(&source_stat, dest); #endif return 0; } #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS if (! S_ISDIR(source_stat.st_mode)) { add_to_ino_dev_hashtable(&source_stat, dest); } #endif end: if (flags & FILEUTILS_PRESERVE_STATUS) { struct utimbuf times; times.actime = source_stat.st_atime; times.modtime = source_stat.st_mtime; if (utime(dest, ×) < 0) bb_perror_msg("unable to preserve times of `%s'", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("unable to preserve ownership of `%s'", dest); } if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("unable to preserve permissions of `%s'", dest); } return status; }
int copy_file(const char *source, const char *dest, int flags) { struct stat source_stat; struct stat dest_stat; int status = 0; signed char dest_exists = 0; signed char ovr; #define FLAGS_DEREF (flags & FILEUTILS_DEREFERENCE) if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { // This may be a dangling symlink. // Making [sym]links to dangling symlinks works, so... if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) goto make_links; bb_perror_msg("cannot stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("cannot stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { bb_error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("omitting directory '%s'", source); return -1; } /* Create DEST. */ if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("target '%s' is not a directory", dest); return -1; } } else { mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("cannot create directory '%s'", dest); return -1; } umask(saved_umask); } /* Recursively copy files in SOURCE. */ dp = opendir(source); if (dp == NULL) { status = -1; goto preserve_status; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if (new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags) < 0) status = -1; free(new_source); free(new_dest); } closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 ) { bb_perror_msg("cannot change permissions of '%s'", dest); status = -1; } } else if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { int (*lf)(const char *oldpath, const char *newpath); make_links: // Hmm... maybe // if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? // (but realpath returns NULL on dangling symlinks...) lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; if (lf(source, dest) < 0) { ovr = retry_overwrite(dest, flags); if (ovr <= 0) return ovr; if (lf(source, dest) < 0) { bb_perror_msg("cannot create link '%s'", dest); return -1; } } return 0; } else if (S_ISREG(source_stat.st_mode) // Huh? DEREF uses stat, which never returns links IIRC... || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) ) { int src_fd; int dst_fd; if (ENABLE_FEATURE_PRESERVE_HARDLINKS) { char *link_name; if (!FLAGS_DEREF && is_in_ino_dev_hashtable(&source_stat, &link_name) ) { if (link(link_name, dest) < 0) { ovr = retry_overwrite(dest, flags); if (ovr <= 0) return ovr; if (link(link_name, dest) < 0) { bb_perror_msg("cannot create link '%s'", dest); return -1; } } return 0; } // TODO: probably is_in_.. and add_to_... // can be combined: find_or_add_... add_to_ino_dev_hashtable(&source_stat, dest); } src_fd = open(source, O_RDONLY); if (src_fd == -1) { bb_perror_msg("cannot open '%s'", source); return -1; } // POSIX: if exists and -i, ask (w/o -i assume yes). // Then open w/o EXCL. // If open still fails and -f, try unlink, then try open again. // Result: a mess: // If dest is a softlink, we overwrite softlink's destination! // (or fail, if it points to dir/nonexistent location/etc). // This is strange, but POSIX-correct. // coreutils cp has --remove-destination to override this... dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE) ? O_WRONLY|O_CREAT|O_TRUNC|O_EXCL : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); if (dst_fd == -1) { // We would not do POSIX insanity. -i asks, // then _unlinks_ the offender. Presto. // Or else we will end up having 3 open()s! ovr = retry_overwrite(dest, flags); if (ovr <= 0) { close(src_fd); return ovr; } dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode); if (dst_fd == -1) { bb_perror_msg("cannot open '%s'", dest); close(src_fd); return -1; } } if (bb_copyfd_eof(src_fd, dst_fd) == -1) status = -1; if (close(dst_fd) < 0) { bb_perror_msg("cannot close '%s'", dest); status = -1; } if (close(src_fd) < 0) { bb_perror_msg("cannot close '%s'", source); status = -1; } } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) || S_ISLNK(source_stat.st_mode) ) { // We are lazy here, a bit lax with races... if (dest_exists) { ovr = retry_overwrite(dest, flags); if (ovr <= 0) return ovr; } if (S_ISFIFO(source_stat.st_mode)) { if (mkfifo(dest, source_stat.st_mode) < 0) { bb_perror_msg("cannot create fifo '%s'", dest); return -1; } } else if (S_ISLNK(source_stat.st_mode)) { char *lpath; lpath = xreadlink(source); if (symlink(lpath, dest) < 0) { bb_perror_msg("cannot create symlink '%s'", dest); free(lpath); return -1; } free(lpath); if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); return 0; } else { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("cannot create '%s'", dest); return -1; } } } else { bb_error_msg("internal error: unrecognized file type"); return -1; } preserve_status: if (flags & FILEUTILS_PRESERVE_STATUS /* Cannot happen: */ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ ) { struct utimbuf times; times.actime = source_stat.st_atime; times.modtime = source_stat.st_mtime; if (utime(dest, ×) < 0) bb_perror_msg("cannot preserve %s of '%s'", "times", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("cannot preserve %s of '%s'", "ownership", dest); } if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("cannot preserve %s of '%s'", "permissions", dest); } return status; }