just whitespace
[oweals/busybox.git] / libbb / copy_file.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini copy_file implementation for busybox
4  *
5  * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  *
9  */
10
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <utime.h>
16 #include <errno.h>
17 #include <dirent.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "busybox.h"
22
23 /* Compiler version-specific crap that should be in a header file somewhere. */
24
25 #if !((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1))
26 #define lchown chown
27 #endif
28
29
30 int copy_file(const char *source, const char *dest, int flags)
31 {
32         struct stat source_stat;
33         struct stat dest_stat;
34         int dest_exists = 0;
35         int status = 0;
36
37         if ((!(flags & FILEUTILS_DEREFERENCE) &&
38                         lstat(source, &source_stat) < 0) ||
39                         ((flags & FILEUTILS_DEREFERENCE) &&
40                          stat(source, &source_stat) < 0)) {
41                 bb_perror_msg("%s", source);
42                 return -1;
43         }
44
45         if (lstat(dest, &dest_stat) < 0) {
46                 if (errno != ENOENT) {
47                         bb_perror_msg("unable to stat `%s'", dest);
48                         return -1;
49                 }
50         } else {
51                 if (source_stat.st_dev == dest_stat.st_dev &&
52                         source_stat.st_ino == dest_stat.st_ino)
53                 {
54                         bb_error_msg("`%s' and `%s' are the same file", source, dest);
55                         return -1;
56                 }
57                 dest_exists = 1;
58         }
59
60         if (S_ISDIR(source_stat.st_mode)) {
61                 DIR *dp;
62                 struct dirent *d;
63                 mode_t saved_umask = 0;
64
65                 if (!(flags & FILEUTILS_RECUR)) {
66                         bb_error_msg("%s: omitting directory", source);
67                         return -1;
68                 }
69
70                 /* Create DEST.  */
71                 if (dest_exists) {
72                         if (!S_ISDIR(dest_stat.st_mode)) {
73                                 bb_error_msg("`%s' is not a directory", dest);
74                                 return -1;
75                         }
76                 } else {
77                         mode_t mode;
78                         saved_umask = umask(0);
79
80                         mode = source_stat.st_mode;
81                         if (!(flags & FILEUTILS_PRESERVE_STATUS))
82                                 mode = source_stat.st_mode & ~saved_umask;
83                         mode |= S_IRWXU;
84
85                         if (mkdir(dest, mode) < 0) {
86                                 umask(saved_umask);
87                                 bb_perror_msg("cannot create directory `%s'", dest);
88                                 return -1;
89                         }
90
91                         umask(saved_umask);
92                 }
93
94                 /* Recursively copy files in SOURCE.  */
95                 if ((dp = opendir(source)) == NULL) {
96                         bb_perror_msg("unable to open directory `%s'", source);
97                         status = -1;
98                         goto preserve_status;
99                 }
100
101                 while ((d = readdir(dp)) != NULL) {
102                         char *new_source, *new_dest;
103
104                         new_source = concat_subpath_file(source, d->d_name);
105                         if(new_source == NULL)
106                                 continue;
107                         new_dest = concat_path_file(dest, d->d_name);
108                         if (copy_file(new_source, new_dest, flags) < 0)
109                                 status = -1;
110                         free(new_source);
111                         free(new_dest);
112                 }
113                 /* closedir have only EBADF error, but "dp" not changes */
114                 closedir(dp);
115
116                 if (!dest_exists &&
117                                 chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
118                         bb_perror_msg("unable to change permissions of `%s'", dest);
119                         status = -1;
120                 }
121         } else if (S_ISREG(source_stat.st_mode) || (flags & FILEUTILS_DEREFERENCE))
122         {
123                 int src_fd;
124                 int dst_fd;
125 #ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
126                 char *link_name;
127
128                 if (!(flags & FILEUTILS_DEREFERENCE) &&
129                                 is_in_ino_dev_hashtable(&source_stat, &link_name)) {
130                         if (link(link_name, dest) < 0) {
131                                 bb_perror_msg("unable to link `%s'", dest);
132                                 return -1;
133                         }
134
135                         return 0;
136                 }
137                 add_to_ino_dev_hashtable(&source_stat, dest);
138 #endif
139                 src_fd = open(source, O_RDONLY);
140                 if (src_fd == -1) {
141                         bb_perror_msg("unable to open `%s'", source);
142                         return(-1);
143                 }
144
145                 if (dest_exists) {
146                         if (flags & FILEUTILS_INTERACTIVE) {
147                                 fprintf(stderr, "%s: overwrite `%s'? ", bb_applet_name, dest);
148                                 if (!bb_ask_confirmation()) {
149                                         close (src_fd);
150                                         return 0;
151                                 }
152                         }
153
154                         dst_fd = open(dest, O_WRONLY|O_TRUNC);
155                         if (dst_fd == -1) {
156                                 if (!(flags & FILEUTILS_FORCE)) {
157                                         bb_perror_msg("unable to open `%s'", dest);
158                                         close(src_fd);
159                                         return -1;
160                                 }
161
162                                 if (unlink(dest) < 0) {
163                                         bb_perror_msg("unable to remove `%s'", dest);
164                                         close(src_fd);
165                                         return -1;
166                                 }
167
168                                 dest_exists = 0;
169                         }
170                 }
171
172                 if (!dest_exists) {
173                         dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode);
174                         if (dst_fd == -1) {
175                                 bb_perror_msg("unable to open `%s'", dest);
176                                 close(src_fd);
177                                 return(-1);
178                         }
179                 }
180
181                 if (bb_copyfd_eof(src_fd, dst_fd) == -1)
182                         status = -1;
183
184                 if (close(dst_fd) < 0) {
185                         bb_perror_msg("unable to close `%s'", dest);
186                         status = -1;
187                 }
188
189                 if (close(src_fd) < 0) {
190                         bb_perror_msg("unable to close `%s'", source);
191                         status = -1;
192                 }
193         } else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
194             S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ||
195             S_ISLNK(source_stat.st_mode)) {
196
197                 if (dest_exists) {
198                         if((flags & FILEUTILS_FORCE) == 0) {
199                                 fprintf(stderr, "`%s' exists\n", dest);
200                                 return -1;
201                         }
202                         if(unlink(dest) < 0) {
203                                 bb_perror_msg("unable to remove `%s'", dest);
204                                 return -1;
205                         }
206                 }
207                 if (S_ISFIFO(source_stat.st_mode)) {
208                         if (mkfifo(dest, source_stat.st_mode) < 0) {
209                                 bb_perror_msg("cannot create fifo `%s'", dest);
210                                 return -1;
211                         }
212                 } else if (S_ISLNK(source_stat.st_mode)) {
213                         char *lpath;
214
215                         lpath = xreadlink(source);
216                         if (symlink(lpath, dest) < 0) {
217                                 bb_perror_msg("cannot create symlink `%s'", dest);
218                                 return -1;
219                         }
220                         free(lpath);
221
222                         if (flags & FILEUTILS_PRESERVE_STATUS)
223                                 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
224                                         bb_perror_msg("unable to preserve ownership of `%s'", dest);
225
226                         return 0;
227
228                 } else {
229                         if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
230                                 bb_perror_msg("unable to create `%s'", dest);
231                                 return -1;
232                         }
233                 }
234         } else {
235                 bb_error_msg("internal error: unrecognized file type");
236                 return -1;
237         }
238
239 preserve_status:
240
241         if (flags & FILEUTILS_PRESERVE_STATUS) {
242                 struct utimbuf times;
243                 char *msg="unable to preserve %s of `%s'";
244
245                 times.actime = source_stat.st_atime;
246                 times.modtime = source_stat.st_mtime;
247                 if (utime(dest, &times) < 0)
248                         bb_perror_msg(msg, "times", dest);
249                 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
250                         source_stat.st_mode &= ~(S_ISUID | S_ISGID);
251                         bb_perror_msg(msg, "ownership", dest);
252                 }
253                 if (chmod(dest, source_stat.st_mode) < 0)
254                         bb_perror_msg(msg, "permissions", dest);
255         }
256
257         return status;
258 }