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; }
/* 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; }
/* tiny recursive du */ static long du(char *filename) { struct stat statbuf; long sum; int len; if ((lstat(filename, &statbuf)) != 0) { perror_msg_and_die("%s", filename); } du_depth++; sum = (statbuf.st_blocks << 9); /* Don't add in stuff pointed to by symbolic links */ if (S_ISLNK(statbuf.st_mode)) { sum = 0L; if (du_depth == 1) print(sum, filename); } if (S_ISDIR(statbuf.st_mode)) { DIR *dir; struct dirent *entry; dir = opendir(filename); if (!dir) { du_depth--; return 0; } len = strlen(filename); if (len > 0 && filename[len - 1] == '/') filename[--len] = '\0'; while ((entry = readdir(dir))) { char newfile[BUFSIZ + 1]; char *name = entry->d_name; if ((strcmp(name, "..") == 0) || (strcmp(name, ".") == 0)) { continue; } if (len + strlen(name) + 1 > BUFSIZ) { error_msg(name_too_long); du_depth--; return 0; } sprintf(newfile, "%s/%s", filename, name); sum += du(newfile); } closedir(dir); print(sum, filename); } else if (statbuf.st_nlink > 1 && !count_hardlinks) { /* Add files with hard links only once */ if (is_in_ino_dev_hashtable(&statbuf, NULL)) { sum = 0L; if (du_depth == 1) print(sum, filename); } else { add_to_ino_dev_hashtable(&statbuf, NULL); } } du_depth--; return sum; }
/* tiny recursive du */ static long du(const char * const filename) { struct stat statbuf; long sum; if (lstat(filename, &statbuf) != 0) { bb_perror_msg("%s", filename); status = EXIT_FAILURE; return 0; } if (one_file_system) { if (du_depth == 0) { dir_dev = statbuf.st_dev; } else if (dir_dev != statbuf.st_dev) { return 0; } } sum = statbuf.st_blocks; if (S_ISLNK(statbuf.st_mode)) { if (slink_depth > du_depth) { /* -H or -L */ if (stat(filename, &statbuf) != 0) { bb_perror_msg("%s", filename); status = EXIT_FAILURE; return 0; } sum = statbuf.st_blocks; if (slink_depth == 1) { slink_depth = INT_MAX; /* Convert -H to -L. */ } } } if (statbuf.st_nlink > count_hardlinks) { /* Add files/directories with links only once */ if (is_in_ino_dev_hashtable(&statbuf)) { return 0; } add_to_ino_dev_hashtable(&statbuf, NULL); } if (S_ISDIR(statbuf.st_mode)) { DIR *dir; struct dirent *entry; char *newfile; dir = warn_opendir(filename); if (!dir) { status = EXIT_FAILURE; return sum; } newfile = last_char_is(filename, '/'); if (newfile) *newfile = '\0'; while ((entry = readdir(dir))) { char *name = entry->d_name; newfile = concat_subpath_file(filename, name); if (newfile == NULL) continue; ++du_depth; sum += du(newfile); --du_depth; free(newfile); } closedir(dir); } else if (du_depth > print_files) { return sum; } if (du_depth <= max_print_depth) { print(sum, filename); } return sum; }
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; }