Make 'grep -l' work
[oweals/busybox.git] / libbb / copy_file.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) tons of folks.  Tracking down who wrote what
6  * isn't something I'm going to worry about...  If you wrote something
7  * here, please feel free to acknowledge your work.
8  *
9  * 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
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
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  */
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <utime.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include "libbb.h"
35
36
37 /*
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  */
43 int
44 copy_file(const char *srcName, const char *destName,
45                  int setModes, int followLinks, int forceFlag)
46 {
47         int rfd;
48         int wfd;
49         int status;
50         struct stat srcStatBuf;
51         struct stat dstStatBuf;
52         struct utimbuf times;
53
54         if (followLinks == TRUE)
55                 status = stat(srcName, &srcStatBuf);
56         else
57                 status = lstat(srcName, &srcStatBuf);
58
59         if (status < 0) {
60                 perror_msg("%s", srcName);
61                 return FALSE;
62         }
63
64         if (followLinks == TRUE)
65                 status = stat(destName, &dstStatBuf);
66         else
67                 status = lstat(destName, &dstStatBuf);
68
69         if (status < 0 || forceFlag==TRUE) {
70                 unlink(destName);
71                 dstStatBuf.st_ino = -1;
72                 dstStatBuf.st_dev = -1;
73         }
74
75         if ((srcStatBuf.st_dev == dstStatBuf.st_dev) &&
76                 (srcStatBuf.st_ino == dstStatBuf.st_ino)) {
77                 error_msg("Copying file \"%s\" to itself", srcName);
78                 return FALSE;
79         }
80
81         if (S_ISDIR(srcStatBuf.st_mode)) {
82                 //fprintf(stderr, "copying directory %s to %s\n", srcName, destName);
83                 /* Make sure the directory is writable */
84                 status = mkdir(destName, 0777777 ^ umask(0));
85                 if (status < 0 && errno != EEXIST) {
86                         perror_msg("%s", destName);
87                         return FALSE;
88                 }
89         } else if (S_ISLNK(srcStatBuf.st_mode)) {
90                 char link_val[BUFSIZ + 1];
91                 int link_size;
92
93                 //fprintf(stderr, "copying link %s to %s\n", srcName, destName);
94                 /* Warning: This could possibly truncate silently, to BUFSIZ chars */
95                 link_size = readlink(srcName, &link_val[0], BUFSIZ);
96                 if (link_size < 0) {
97                         perror_msg("%s", srcName);
98                         return FALSE;
99                 }
100                 link_val[link_size] = '\0';
101                 status = symlink(link_val, destName);
102                 if (status < 0) {
103                         perror_msg("%s", destName);
104                         return FALSE;
105                 }
106 #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
107                 if (setModes == TRUE) {
108                         /* Try to set owner, but fail silently like GNU cp */
109                         lchown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid);
110                 }
111 #endif
112                 return TRUE;
113         } else if (S_ISFIFO(srcStatBuf.st_mode)) {
114                 //fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
115                 if (mkfifo(destName, 0644) < 0) {
116                         perror_msg("%s", destName);
117                         return FALSE;
118                 }
119         } else if (S_ISBLK(srcStatBuf.st_mode) || S_ISCHR(srcStatBuf.st_mode)
120                            || S_ISSOCK(srcStatBuf.st_mode)) {
121                 //fprintf(stderr, "copying soc, blk, or chr %s to %s\n", srcName, destName);
122                 if (mknod(destName, srcStatBuf.st_mode, srcStatBuf.st_rdev) < 0) {
123                         perror_msg("%s", destName);
124                         return FALSE;
125                 }
126         } else if (S_ISREG(srcStatBuf.st_mode)) {
127                 //fprintf(stderr, "copying regular file %s to %s\n", srcName, destName);
128                 rfd = open(srcName, O_RDONLY);
129                 if (rfd < 0) {
130                         perror_msg("%s", srcName);
131                         return FALSE;
132                 }
133
134                 wfd = open(destName, O_WRONLY | O_CREAT | O_TRUNC,
135                                  srcStatBuf.st_mode);
136                 if (wfd < 0) {
137                         perror_msg("%s", destName);
138                         close(rfd);
139                         return FALSE;
140                 }
141
142                 if (copy_file_chunk(rfd, wfd, srcStatBuf.st_size)==FALSE)
143                         goto error_exit;        
144                 
145                 close(rfd);
146                 if (close(wfd) < 0) {
147                         return FALSE;
148                 }
149         }
150
151         if (setModes == TRUE) {
152                 /* This is fine, since symlinks never get here */
153                 if (chown(destName, srcStatBuf.st_uid, srcStatBuf.st_gid) < 0)
154                         perror_msg_and_die("%s", destName);
155                 if (chmod(destName, srcStatBuf.st_mode) < 0)
156                         perror_msg_and_die("%s", destName);
157                 times.actime = srcStatBuf.st_atime;
158                 times.modtime = srcStatBuf.st_mtime;
159                 if (utime(destName, &times) < 0)
160                         perror_msg_and_die("%s", destName);
161         }
162
163         return TRUE;
164
165   error_exit:
166         perror_msg("%s", destName);
167         close(rfd);
168         close(wfd);
169
170         return FALSE;
171 }
172
173 /* END CODE */
174 /*
175 Local Variables:
176 c-file-style: "linux"
177 c-basic-offset: 4
178 tab-width: 4
179 End:
180 */