blob: 3aaa00fbb95a31e61c66e022b24a615d9960a9d9 [file] [log] [blame]
Rob Landley6e6871c2008-02-20 01:47:56 -06001/* vi: set sw=4 ts=4:
2 *
3 * cp.c - Copy files.
4 *
5 * Copyright 2008 Rob Landley <rob@landley.net>
6 *
7 * See http://www.opengroup.org/onlinepubs/009695399/utilities/cp.html
8 *
9 * "R+ra+d+p+r"
Rob Landleydd4d83e2008-04-09 22:01:20 -050010USE_CP(NEWTOY(cp, "<2vslrR+rdpa+d+p+rHLPif", TOYFLAG_BIN))
Rob Landley6e6871c2008-02-20 01:47:56 -060011
12config CP
13 bool "cp"
Rob Landley4416cae2008-03-24 05:14:37 -050014 default y
Rob Landley6e6871c2008-02-20 01:47:56 -060015 help
Rob Landley4416cae2008-03-24 05:14:37 -050016 usage: cp -fiprdal SOURCE... DEST
Rob Landley6e6871c2008-02-20 01:47:56 -060017
18 Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
19 be a directory.
20
21 -f force copy by deleting destination file
22 -i interactive, prompt before overwriting existing DEST
23 -p preserve timestamps, ownership, and permissions
24 -r recurse into subdirectories (DEST must be a directory)
Rob Landley4416cae2008-03-24 05:14:37 -050025 -d don't dereference symlinks
26 -a same as -dpr
27 -l hard link instead of copying
Rob Landleydd4d83e2008-04-09 22:01:20 -050028 -v verbose
Rob Landley6e6871c2008-02-20 01:47:56 -060029*/
30
31#include "toys.h"
32
33#define FLAG_f 1
Rob Landleyde695272008-02-24 03:48:06 -060034#define FLAG_i 2 // todo
35#define FLAG_P 4 // todo
36#define FLAG_L 8 // todo
37#define FLAG_H 16 // todo
Rob Landley6e6871c2008-02-20 01:47:56 -060038#define FLAG_a 32
39#define FLAG_p 64
Rob Landleyde695272008-02-24 03:48:06 -060040#define FLAG_d 128 // todo
Rob Landley6e6871c2008-02-20 01:47:56 -060041#define FLAG_R 256
42#define FLAG_r 512
Rob Landley4416cae2008-03-24 05:14:37 -050043#define FLAG_l 1024 // todo
44#define FLAG_s 2048 // todo
Rob Landleydd4d83e2008-04-09 22:01:20 -050045#define FLAG_v 4098
Rob Landley6e6871c2008-02-20 01:47:56 -060046
47DEFINE_GLOBALS(
48 char *destname;
49 int destisdir;
Rob Landley7f184fa2008-02-21 04:44:42 -060050 int destisnew;
Rob Landley4416cae2008-03-24 05:14:37 -050051 int keep_symlinks;
Rob Landley6e6871c2008-02-20 01:47:56 -060052)
53
54#define TT this.cp
55
56// Copy an individual file or directory to target.
57
Rob Landley4416cae2008-03-24 05:14:37 -050058void cp_file(char *src, char *dst, struct stat *srcst)
Rob Landley6e6871c2008-02-20 01:47:56 -060059{
Rob Landleyde695272008-02-24 03:48:06 -060060 int fdout = -1;
Rob Landley6e6871c2008-02-20 01:47:56 -060061
Rob Landleydd4d83e2008-04-09 22:01:20 -050062 if (toys.optflags & FLAG_v)
63 printf("'%s' -> '%s'\n", src, dst);
64
Rob Landley6e6871c2008-02-20 01:47:56 -060065 // Copy directory or file to destination.
Rob Landley7f184fa2008-02-21 04:44:42 -060066
Rob Landley6e6871c2008-02-20 01:47:56 -060067 if (S_ISDIR(srcst->st_mode)) {
Rob Landley7f184fa2008-02-21 04:44:42 -060068 struct stat st2;
69
70 // Always make directory writeable to us, so we can create files in it.
71 //
72 // Yes, there's a race window between mkdir() and open() so it's
73 // possible that -p can be made to chown a directory other than the one
74 // we created. The closest we can do to closing this is make sure
75 // that what we open _is_ a directory rather than something else.
76
Rob Landley50c8c022008-03-26 00:54:56 -050077 if ((mkdir(dst, srcst->st_mode | 0200) && errno != EEXIST)
78 || 0>(fdout=open(dst, 0)) || fstat(fdout, &st2)
79 || !S_ISDIR(st2.st_mode))
Rob Landley7f184fa2008-02-21 04:44:42 -060080 {
Rob Landley4416cae2008-03-24 05:14:37 -050081 perror_exit("mkdir '%s'", dst);
Rob Landley7f184fa2008-02-21 04:44:42 -060082 }
Rob Landley4416cae2008-03-24 05:14:37 -050083 } else if (TT.keep_symlinks && S_ISLNK(srcst->st_mode)) {
Rob Landleyde695272008-02-24 03:48:06 -060084 char *link = xreadlink(src);
85
86 // Note: -p currently has no effect on symlinks. How do you get a
87 // filehandle to them? O_NOFOLLOW causes the open to fail.
Rob Landley4416cae2008-03-24 05:14:37 -050088 if (!link || symlink(link, dst)) perror_msg("link '%s'", dst);
Rob Landleyde695272008-02-24 03:48:06 -060089 free(link);
Rob Landleyf639c652008-04-09 22:24:36 -050090 return;
Rob Landleyde695272008-02-24 03:48:06 -060091 } else if (toys.optflags & FLAG_l) {
Rob Landley4416cae2008-03-24 05:14:37 -050092 if (link(src, dst)) perror_msg("link '%s'");
93 return;
Rob Landley6e6871c2008-02-20 01:47:56 -060094 } else {
Rob Landley7f184fa2008-02-21 04:44:42 -060095 int fdin, i;
96
Rob Landley6e6871c2008-02-20 01:47:56 -060097 fdin = xopen(src, O_RDONLY);
Rob Landley7f184fa2008-02-21 04:44:42 -060098 for (i=2 ; i; i--) {
Rob Landley4416cae2008-03-24 05:14:37 -050099 fdout = open(dst, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
Rob Landley7f184fa2008-02-21 04:44:42 -0600100 if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
Rob Landley4416cae2008-03-24 05:14:37 -0500101 unlink(dst);
Rob Landley7f184fa2008-02-21 04:44:42 -0600102 }
Rob Landley4416cae2008-03-24 05:14:37 -0500103 if (fdout<0) perror_exit("%s", dst);
Rob Landley6e6871c2008-02-20 01:47:56 -0600104 xsendfile(fdin, fdout);
105 close(fdin);
Rob Landley6e6871c2008-02-20 01:47:56 -0600106 }
Rob Landley7f184fa2008-02-21 04:44:42 -0600107
108 // Inability to set these isn't fatal, some require root access.
109 // Can't do fchmod() etc here because -p works on mkdir, too.
110
111 if (toys.optflags & FLAG_p) {
Rob Landley7471b562008-12-14 01:08:37 -0600112 int mask = umask(0), ignored;
Rob Landley7f184fa2008-02-21 04:44:42 -0600113 struct utimbuf ut;
114
Rob Landley7471b562008-12-14 01:08:37 -0600115 ignored = fchown(fdout,srcst->st_uid, srcst->st_gid);
Rob Landley7f184fa2008-02-21 04:44:42 -0600116 ut.actime = srcst->st_atime;
117 ut.modtime = srcst->st_mtime;
Rob Landley4416cae2008-03-24 05:14:37 -0500118 utime(dst, &ut);
Rob Landley7f184fa2008-02-21 04:44:42 -0600119 umask(mask);
120 }
121 xclose(fdout);
Rob Landley6e6871c2008-02-20 01:47:56 -0600122}
123
124// Callback from dirtree_read() for each file/directory under a source dir.
125
Rob Landley7f184fa2008-02-21 04:44:42 -0600126int cp_node(char *path, struct dirtree *node)
Rob Landley6e6871c2008-02-20 01:47:56 -0600127{
Rob Landley7f184fa2008-02-21 04:44:42 -0600128 char *s = path+strlen(path);
Rob Landley4416cae2008-03-24 05:14:37 -0500129 struct dirtree *n;
Rob Landley7f184fa2008-02-21 04:44:42 -0600130
131 // Find appropriate chunk of path for destination.
132
Rob Landley5ba66632008-03-25 17:09:40 -0500133 n = node;
134 if (!TT.destisdir) n = n->parent;
135 for (;;n = n->parent) {
Rob Landley4416cae2008-03-24 05:14:37 -0500136 while (s!=path) {
137 if (*(--s)=='/') break;
Rob Landley7f184fa2008-02-21 04:44:42 -0600138 }
Rob Landley4416cae2008-03-24 05:14:37 -0500139 if (!n) break;
Rob Landley7f184fa2008-02-21 04:44:42 -0600140 }
Rob Landley4416cae2008-03-24 05:14:37 -0500141 if (s != path) s++;
142
143 s = xmsprintf("%s/%s", TT.destname, s);
144 cp_file(path, s, &(node->st));
145 free(s);
146
Rob Landley6e6871c2008-02-20 01:47:56 -0600147 return 0;
148}
149
150void cp_main(void)
151{
152 struct stat st;
153 int i;
154
155 // Grab target argument. (Guaranteed to be there due to "<2" above.)
156
157 TT.destname = toys.optargs[--toys.optc];
158
159 // If destination doesn't exist, are we ok with that?
Rob Landley7f184fa2008-02-21 04:44:42 -0600160
Rob Landley6e6871c2008-02-20 01:47:56 -0600161 if (stat(TT.destname, &st)) {
162 if (toys.optc>1) goto error_notdir;
Rob Landley7f184fa2008-02-21 04:44:42 -0600163 TT.destisnew++;
Rob Landley6e6871c2008-02-20 01:47:56 -0600164
165 // If destination exists...
Rob Landley7f184fa2008-02-21 04:44:42 -0600166
Rob Landley6e6871c2008-02-20 01:47:56 -0600167 } else {
168 if (S_ISDIR(st.st_mode)) TT.destisdir++;
169 else if (toys.optc > 1) goto error_notdir;
170 }
171
172 // Handle sources
Rob Landley7f184fa2008-02-21 04:44:42 -0600173
Rob Landley6e6871c2008-02-20 01:47:56 -0600174 for (i=0; i<toys.optc; i++) {
175 char *src = toys.optargs[i];
Rob Landley4416cae2008-03-24 05:14:37 -0500176 char *dst;
Rob Landley6e6871c2008-02-20 01:47:56 -0600177
Rob Landley4416cae2008-03-24 05:14:37 -0500178 // Skip src==dest (should check inodes to catch "cp blah ./blah").
Rob Landley7f184fa2008-02-21 04:44:42 -0600179
180 if (!strcmp(src, TT.destname)) continue;
Rob Landley4416cae2008-03-24 05:14:37 -0500181
182 // Skip nonexistent sources.
183
184 TT.keep_symlinks = toys.optflags & FLAG_d;
185 if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st))
Rob Landley7f184fa2008-02-21 04:44:42 -0600186 {
Rob Landley6e6871c2008-02-20 01:47:56 -0600187 perror_msg("'%s'", src);
188 toys.exitval = 1;
189 continue;
190 }
191
192 // Copy directory or file.
Rob Landley7f184fa2008-02-21 04:44:42 -0600193
Rob Landley5ba66632008-03-25 17:09:40 -0500194 if (TT.destisdir) {
195 dst = strrchr(src, '/');
196 if (dst) dst++;
197 else dst=src;
198 dst = xmsprintf("%s/%s", TT.destname, dst);
199 } else dst = TT.destname;
Rob Landley6e6871c2008-02-20 01:47:56 -0600200 if (S_ISDIR(st.st_mode)) {
201 if (toys.optflags & FLAG_r) {
Rob Landley4416cae2008-03-24 05:14:37 -0500202 cp_file(src, dst, &st);
203
204 TT.keep_symlinks++;
Rob Landley7f184fa2008-02-21 04:44:42 -0600205 strncpy(toybuf, src, sizeof(toybuf)-1);
206 toybuf[sizeof(toybuf)-1]=0;
207 dirtree_read(toybuf, NULL, cp_node);
Rob Landley6e6871c2008-02-20 01:47:56 -0600208 } else error_msg("Skipped dir '%s'", src);
Rob Landley4416cae2008-03-24 05:14:37 -0500209 } else cp_file(src, dst, &st);
210 if (TT.destisdir) free(dst);
Rob Landley6e6871c2008-02-20 01:47:56 -0600211 }
Rob Landley7f184fa2008-02-21 04:44:42 -0600212
Rob Landley6e6871c2008-02-20 01:47:56 -0600213 return;
214
215error_notdir:
216 error_exit("'%s' isn't a directory", TT.destname);
217}