aboutsummaryrefslogtreecommitdiff
path: root/libbb/copy_file.c
diff options
context:
space:
mode:
authorMatt Kraai <kraai@debian.org>2001-04-23 18:53:07 +0000
committerMatt Kraai <kraai@debian.org>2001-04-23 18:53:07 +0000
commit91b2855ba8b9918b79dbe4b9188a3acccb41f7b7 (patch)
treeb6ded0b21428442ba27167d8654410c8ce0c73d3 /libbb/copy_file.c
parent4e9267d76c7dc47064bc80b1f8542453725158d7 (diff)
downloadbusybox-w32-91b2855ba8b9918b79dbe4b9188a3acccb41f7b7.tar.gz
busybox-w32-91b2855ba8b9918b79dbe4b9188a3acccb41f7b7.tar.bz2
busybox-w32-91b2855ba8b9918b79dbe4b9188a3acccb41f7b7.zip
Rewrite cp and mv to be SUSv2 compliant.
Diffstat (limited to 'libbb/copy_file.c')
-rw-r--r--libbb/copy_file.c332
1 files changed, 189 insertions, 143 deletions
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 7e5d11e67..113c253b9 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -1,10 +1,9 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * Utility routines. 3 * Mini copy_file implementation for busybox
4 * 4 *
5 * Copyright (C) tons of folks. Tracking down who wrote what 5 *
6 * isn't something I'm going to worry about... If you wrote something 6 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
7 * here, please feel free to acknowledge your work.
8 * 7 *
9 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by 9 * it under the terms of the GNU General Public License as published by
@@ -20,179 +19,226 @@
20 * along with this program; if not, write to the Free Software 19 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * 21 *
23 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
24 * Permission has been granted to redistribute this code under the GPL.
25 *
26 */ 22 */
27 23
28#include <stdio.h> 24#include <sys/types.h>
29#include <errno.h> 25#include <sys/stat.h>
30#include <utime.h>
31#include <unistd.h> 26#include <unistd.h>
32#include <fcntl.h> 27#include <fcntl.h>
33#include <sys/stat.h> 28#include <utime.h>
34#include "libbb.h" 29#include <errno.h>
30#include <dirent.h>
31#include <stdlib.h>
35 32
33#include "libbb.h"
36 34
37/* 35int copy_file(const char *source, const char *dest, int flags)
38 * Copy one file to another, while possibly preserving its modes, times, and
39 * modes. Returns TRUE if successful, or FALSE on a failure with an error
40 * message output. (Failure is not indicated if attributes cannot be set.)
41 * -Erik Andersen
42 */
43int
44copy_file(const char *src_name, const char *dst_name,
45 int set_modes, int follow_links, int force_flag, int quiet_flag)
46{ 36{
47 FILE *src_file = NULL; 37 struct stat source_stat;
48 FILE *dst_file = NULL; 38 struct stat dest_stat;
49 struct stat srcStatBuf; 39 int dest_exists = 1;
50 struct stat dstStatBuf; 40 int status = 0;
51 struct utimbuf times; 41
52 int src_status; 42 if (((flags & CP_PRESERVE_SYMLINKS) && lstat(source, &source_stat) < 0) ||
53 int dst_status; 43 (!(flags & CP_PRESERVE_SYMLINKS) &&
54 44 stat(source, &source_stat) < 0)) {
55 if (follow_links == TRUE) { 45 perror_msg("%s", source);
56 src_status = stat(src_name, &srcStatBuf); 46 return -1;
57 dst_status = stat(dst_name, &dstStatBuf);
58 } else {
59 src_status = lstat(src_name, &srcStatBuf);
60 dst_status = lstat(dst_name, &dstStatBuf);
61 } 47 }
62 48
63 if (src_status < 0) { 49 if (stat(dest, &dest_stat) < 0) {
64 if (!quiet_flag) { 50 if (errno != ENOENT) {
65 perror_msg("%s", src_name); 51 perror_msg("unable to stat `%s'", dest);
52 return -1;
66 } 53 }
67 return FALSE; 54 dest_exists = 0;
68 } 55 }
69 56
70 if ((dst_status < 0) || force_flag) { 57 if (dest_exists && source_stat.st_rdev == dest_stat.st_rdev &&
71 unlink(dst_name); 58 source_stat.st_ino == dest_stat.st_ino) {
72 dstStatBuf.st_ino = -1; 59 error_msg("`%s' and `%s' are the same file", source, dest);
73 dstStatBuf.st_dev = -1; 60 return -1;
74 } 61 }
75 62
76 if ((srcStatBuf.st_dev == dstStatBuf.st_dev) && 63 if (S_ISDIR(source_stat.st_mode)) {
77 (srcStatBuf.st_ino == dstStatBuf.st_ino)) { 64 DIR *dp;
78 if (!quiet_flag) { 65 struct dirent *d;
79 error_msg("Copying file \"%s\" to itself", src_name); 66
67 if (!(flags & CP_RECUR)) {
68 error_msg("%s: omitting directory", source);
69 return -1;
80 } 70 }
81 return FALSE;
82 }
83 71
84 if (S_ISDIR(srcStatBuf.st_mode)) { 72 /* Create DEST. */
85 //fprintf(stderr, "copying directory %s to %s\n", srcName, destName); 73 if (dest_exists) {
86 /* Make sure the directory is writable */ 74 if (!S_ISDIR(dest_stat.st_mode)) {
87 dst_status = create_path(dst_name, 0777777 ^ umask(0)); 75 error_msg("`%s' is not a directory", dest);
88 if ((dst_status < 0) && (errno != EEXIST)) { 76 return -1;
89 if (!quiet_flag) {
90 perror_msg("%s", dst_name);
91 } 77 }
92 return FALSE; 78 } else {
93 } 79 mode_t mode, saved_umask;
94 } else if (S_ISLNK(srcStatBuf.st_mode)) { 80 saved_umask = umask(0);
95 char link_val[BUFSIZ + 1]; 81
96 int link_size; 82 mode = source_stat.st_mode;
97 83 if (!(flags & CP_PRESERVE_STATUS))
98 //fprintf(stderr, "copying link %s to %s\n", srcName, destName); 84 mode = source_stat.st_mode & ~saved_umask;
99 /* Warning: This could possibly truncate silently, to BUFSIZ chars */ 85 mode |= S_IRWXU;
100 link_size = readlink(src_name, &link_val[0], BUFSIZ); 86
101 if (link_size < 0) { 87 if (mkdir(dest, mode) < 0) {
102 if (quiet_flag) { 88 umask(saved_umask);
103 perror_msg("%s", src_name); 89 perror_msg("cannot create directory `%s'", dest);
90 return -1;
104 } 91 }
105 return FALSE; 92
93 umask(saved_umask);
106 } 94 }
107 link_val[link_size] = '\0'; 95
108 src_status = symlink(link_val, dst_name); 96 /* Recursively copy files in SOURCE. */
109 if (src_status < 0) { 97 if ((dp = opendir(source)) == NULL) {
110 if (!quiet_flag) { 98 perror_msg("unable to open directory `%s'", source);
111 perror_msg("%s", dst_name); 99 status = -1;
112 } 100 goto end;
113 return FALSE;
114 } 101 }
115#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) 102
116 if (set_modes == TRUE) { 103 while ((d = readdir(dp)) != NULL) {
117 /* Try to set owner, but fail silently like GNU cp */ 104 char *new_source, *new_dest;
118 lchown(dst_name, srcStatBuf.st_uid, srcStatBuf.st_gid); 105
106 if (strcmp(d->d_name, ".") == 0 ||
107 strcmp(d->d_name, "..") == 0)
108 continue;
109
110 new_source = concat_path_file(source, d->d_name);
111 new_dest = concat_path_file(dest, d->d_name);
112 if (copy_file(new_source, new_dest, flags) < 0)
113 status = -1;
114 free(new_source);
115 free(new_dest);
119 } 116 }
120#endif 117
121 return TRUE; 118 /* ??? What if an error occurs in readdir? */
122 } else if (S_ISFIFO(srcStatBuf.st_mode)) { 119
123 //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName); 120 if (closedir(dp) < 0) {
124 if (mkfifo(dst_name, 0644) < 0) { 121 perror_msg("unable to close directory `%s'", source);
125 if (!quiet_flag) { 122 status = -1;
126 perror_msg("%s", dst_name); 123 }
127 } 124 } else if (S_ISREG(source_stat.st_mode)) {
128 return FALSE; 125 FILE *sfp, *dfp;
129 } 126
130 } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode) 127 if (dest_exists) {
131 || S_ISSOCK(srcStatBuf.st_mode)) { 128 if (flags & CP_INTERACTIVE) {
132 //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName); 129 fprintf(stderr, "cp: overwrite `%s'? ", dest);
133 if (mknod(dst_name, srcStatBuf.st_mode, srcStatBuf.st_rdev) < 0) { 130 if (!ask_confirmation())
134 if (!quiet_flag) { 131 return 0;
135 perror_msg("%s", dst_name);
136 } 132 }
137 return FALSE; 133
138 } 134 if ((dfp = fopen(dest, "w")) == NULL) {
139 } else if (S_ISREG(srcStatBuf.st_mode)) { 135 if (!(flags & CP_FORCE)) {
140 //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName); 136 perror_msg("unable to open `%s'", dest);
141 src_file = fopen(src_name, "r"); 137 return -1;
142 if (src_file == NULL) { 138 }
143 if (!quiet_flag) { 139
144 perror_msg("%s", src_name); 140 if (unlink(dest) < 0) {
141 perror_msg("unable to remove `%s'", dest);
142 return -1;
143 }
144
145 dest_exists = 0;
145 } 146 }
146 return FALSE;
147 } 147 }
148 148
149 dst_file = fopen(dst_name, "w"); 149 if (!dest_exists) {
150 chmod(dst_name, srcStatBuf.st_mode); 150 int fd;
151 if (dst_file == NULL) { 151
152 if (!quiet_flag) { 152 if ((fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode)) < 0 ||
153 perror_msg("%s", dst_name); 153 (dfp = fdopen(fd, "w")) == NULL) {
154 if (fd >= 0)
155 close(fd);
156 perror_msg("unable to open `%s'", dest);
157 return -1;
154 } 158 }
155 fclose(src_file);
156 return FALSE;
157 } 159 }
158 160
159 if (copy_file_chunk(src_file, dst_file, srcStatBuf.st_size)==FALSE) { 161 if ((sfp = fopen(source, "r")) == NULL) {
160 goto error_exit; 162 fclose(dfp);
163 perror_msg("unable to open `%s'", source);
164 status = -1;
165 goto end;
166 }
167
168 copy_file_chunk(sfp, dfp, source_stat.st_size);
169
170 if (fclose(dfp) < 0) {
171 perror_msg("unable to close `%s'", dest);
172 status = -1;
161 } 173 }
162 174
163 fclose(src_file); 175 if (fclose(sfp) < 0) {
164 if (fclose(dst_file) < 0) { 176 perror_msg("unable to close `%s'", source);
165 return FALSE; 177 status = -1;
166 } 178 }
167 } 179 } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
180 S_ISSOCK(source_stat.st_mode)) {
181 if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
182 perror_msg("unable to create `%s'", dest);
183 return -1;
184 }
185 } else if (S_ISFIFO(source_stat.st_mode)) {
186 mode_t mode, saved_umask;
187 saved_umask = umask(0);
188
189 mode = source_stat.st_mode;
190 if (!(flags & CP_PRESERVE_STATUS))
191 mode = source_stat.st_mode & ~saved_umask;
192 mode |= S_IRWXU;
193
194 if (mkfifo(dest, mode) < 0) {
195 umask(saved_umask);
196 perror_msg("cannot create fifo `%s'", dest);
197 return -1;
198 }
199
200 umask(saved_umask);
201 } else if (S_ISLNK(source_stat.st_mode)) {
202 char buf[BUFSIZ + 1];
168 203
169 if (set_modes == TRUE) { 204 if (readlink(source, buf, BUFSIZ) < 0) {
170 /* This is fine, since symlinks never get here */ 205 perror_msg("cannot read `%s'", source);
171 if (chown(dst_name, srcStatBuf.st_uid, srcStatBuf.st_gid) < 0) 206 return -1;
172 perror_msg("%s", dst_name); 207 }
173 if (chmod(dst_name, srcStatBuf.st_mode) < 0) 208 buf[BUFSIZ] = '\0';
174 perror_msg("%s", dst_name); 209
175 times.actime = srcStatBuf.st_atime; 210 if (symlink(buf, dest) < 0) {
176 times.modtime = srcStatBuf.st_mtime; 211 perror_msg("cannot create symlink `%s'", dest);
177 if (utime(dst_name, &times) < 0) 212 return -1;
178 perror_msg("%s", dst_name); 213 }
214
215#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
216 if (flags & CP_PRESERVE_STATUS)
217 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
218 perror_msg("unable to preserve ownership of `%s'", dest);
219#endif
220 return 0;
221 } else {
222 error_msg("internal error: unrecognized file type");
223 return -1;
179 } 224 }
180 225
181 return TRUE; 226end:
182 227
183error_exit: 228 if (flags & CP_PRESERVE_STATUS) {
184 perror_msg("%s", dst_name); 229 struct utimbuf times;
185 fclose(src_file);
186 fclose(dst_file);
187 230
188 return FALSE; 231 times.actime = source_stat.st_atime;
189} 232 times.modtime = source_stat.st_mtime;
233 if (utime(dest, &times) < 0)
234 perror_msg("unable to preserve times of `%s'", dest);
235 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
236 source_stat.st_mode &= ~(S_ISUID | S_ISGID);
237 perror_msg("unable to preserve ownership of `%s'", dest);
238 }
239 if (chmod(dest, source_stat.st_mode) < 0)
240 perror_msg("unable to preserve permissions of `%s'", dest);
241 }
190 242
191/* END CODE */ 243 return 0;
192/* 244}
193Local Variables:
194c-file-style: "linux"
195c-basic-offset: 4
196tab-width: 4
197End:
198*/