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