blob: 4809a7780934b96722ea83ba488ab421aba819b6 [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 Landleyde695272008-02-24 03:48:06 -060010USE_CP(NEWTOY(cp, "<2slrR+rdpa+d+p+rHLPif", TOYFLAG_BIN))
Rob Landley6e6871c2008-02-20 01:47:56 -060011
12config CP
13 bool "cp"
14 default n
15 help
Rob Landleyde695272008-02-24 03:48:06 -060016 usage: cp -fpr 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)
25*/
26
27#include "toys.h"
28
29#define FLAG_f 1
Rob Landleyde695272008-02-24 03:48:06 -060030#define FLAG_i 2 // todo
31#define FLAG_P 4 // todo
32#define FLAG_L 8 // todo
33#define FLAG_H 16 // todo
Rob Landley6e6871c2008-02-20 01:47:56 -060034#define FLAG_a 32
35#define FLAG_p 64
Rob Landleyde695272008-02-24 03:48:06 -060036#define FLAG_d 128 // todo
Rob Landley6e6871c2008-02-20 01:47:56 -060037#define FLAG_R 256
38#define FLAG_r 512
Rob Landleyde695272008-02-24 03:48:06 -060039#define FLAG_s 1024 // todo
40#define FLAG_l 2048 // todo
Rob Landley6e6871c2008-02-20 01:47:56 -060041
42DEFINE_GLOBALS(
43 char *destname;
44 int destisdir;
Rob Landley7f184fa2008-02-21 04:44:42 -060045 int destisnew;
Rob Landley6e6871c2008-02-20 01:47:56 -060046)
47
48#define TT this.cp
49
50// Copy an individual file or directory to target.
51
Rob Landleyde695272008-02-24 03:48:06 -060052void cp_file(char *src, struct stat *srcst, int depth)
Rob Landley6e6871c2008-02-20 01:47:56 -060053{
54 char *s = NULL;
Rob Landleyde695272008-02-24 03:48:06 -060055 int fdout = -1;
Rob Landley6e6871c2008-02-20 01:47:56 -060056
57 // Trim path from name if necessary.
Rob Landley7f184fa2008-02-21 04:44:42 -060058
Rob Landleyde695272008-02-24 03:48:06 -060059 if (!depth) s = strrchr(src, '/');
60 if (s) s++;
61 else s=src;
Rob Landley6e6871c2008-02-20 01:47:56 -060062
63 // Determine location to create new file/directory at.
Rob Landley7f184fa2008-02-21 04:44:42 -060064
Rob Landleyde695272008-02-24 03:48:06 -060065 if (TT.destisdir || depth) s = xmsprintf("%s/%s", TT.destname, s);
Rob Landley6e6871c2008-02-20 01:47:56 -060066 else s = xstrdup(TT.destname);
67
68 // Copy directory or file to destination.
Rob Landley7f184fa2008-02-21 04:44:42 -060069
Rob Landley6e6871c2008-02-20 01:47:56 -060070 if (S_ISDIR(srcst->st_mode)) {
Rob Landley7f184fa2008-02-21 04:44:42 -060071 struct stat st2;
72
73 // Always make directory writeable to us, so we can create files in it.
74 //
75 // Yes, there's a race window between mkdir() and open() so it's
76 // possible that -p can be made to chown a directory other than the one
77 // we created. The closest we can do to closing this is make sure
78 // that what we open _is_ a directory rather than something else.
79
80 if (mkdir(s, srcst->st_mode | 0200) || 0>(fdout=open(s, 0))
81 || fstat(fdout, &st2) || !S_ISDIR(st2.st_mode))
82 {
83 perror_exit("mkdir '%s'", s);
84 }
Rob Landleyde695272008-02-24 03:48:06 -060085 } else if ((depth || (toys.optflags & FLAG_d)) && S_ISLNK(srcst->st_mode)) {
86 struct stat st2;
87 char *link = xreadlink(src);
88
89 // Note: -p currently has no effect on symlinks. How do you get a
90 // filehandle to them? O_NOFOLLOW causes the open to fail.
91 if (!link || symlink(link, s)) perror_msg("link '%s'",s);
92 free(link);
93 } else if (toys.optflags & FLAG_l) {
94 if (link(src, s)) perror_msg("link '%s'");
Rob Landley6e6871c2008-02-20 01:47:56 -060095 } else {
Rob Landley7f184fa2008-02-21 04:44:42 -060096 int fdin, i;
97
Rob Landley6e6871c2008-02-20 01:47:56 -060098 fdin = xopen(src, O_RDONLY);
Rob Landley7f184fa2008-02-21 04:44:42 -060099 for (i=2 ; i; i--) {
100 fdout = open(s, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
101 if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
102 unlink(s);
103 }
104 if (fdout<0) perror_exit("%s", s);
Rob Landley6e6871c2008-02-20 01:47:56 -0600105 xsendfile(fdin, fdout);
106 close(fdin);
Rob Landley6e6871c2008-02-20 01:47:56 -0600107 }
Rob Landley7f184fa2008-02-21 04:44:42 -0600108
109 // Inability to set these isn't fatal, some require root access.
110 // Can't do fchmod() etc here because -p works on mkdir, too.
111
112 if (toys.optflags & FLAG_p) {
113 int mask = umask(0);
114 struct utimbuf ut;
115
116 fchown(fdout,srcst->st_uid, srcst->st_gid);
117 ut.actime = srcst->st_atime;
118 ut.modtime = srcst->st_mtime;
119 utime(s, &ut);
120 umask(mask);
121 }
122 xclose(fdout);
123 free(s);
Rob Landley6e6871c2008-02-20 01:47:56 -0600124}
125
126// Callback from dirtree_read() for each file/directory under a source dir.
127
Rob Landley7f184fa2008-02-21 04:44:42 -0600128int cp_node(char *path, struct dirtree *node)
Rob Landley6e6871c2008-02-20 01:47:56 -0600129{
Rob Landley7f184fa2008-02-21 04:44:42 -0600130 char *s = path+strlen(path);
131 struct dirtree *n = node;
Rob Landleyde695272008-02-24 03:48:06 -0600132 int depth = 0;
Rob Landley7f184fa2008-02-21 04:44:42 -0600133
134 // Find appropriate chunk of path for destination.
135
136 for (;;) {
137 if (*(--s) == '/') {
Rob Landleyde695272008-02-24 03:48:06 -0600138 depth++;
Rob Landley7f184fa2008-02-21 04:44:42 -0600139 if (!n->parent) break;
140 n = n->parent;
141 }
142 }
143 s++;
144
Rob Landleyde695272008-02-24 03:48:06 -0600145 cp_file(s, &(node->st), depth);
Rob Landley6e6871c2008-02-20 01:47:56 -0600146 return 0;
147}
148
149void cp_main(void)
150{
151 struct stat st;
152 int i;
153
154 // Grab target argument. (Guaranteed to be there due to "<2" above.)
155
156 TT.destname = toys.optargs[--toys.optc];
157
158 // If destination doesn't exist, are we ok with that?
Rob Landley7f184fa2008-02-21 04:44:42 -0600159
Rob Landley6e6871c2008-02-20 01:47:56 -0600160 if (stat(TT.destname, &st)) {
161 if (toys.optc>1) goto error_notdir;
Rob Landley7f184fa2008-02-21 04:44:42 -0600162 TT.destisnew++;
Rob Landley6e6871c2008-02-20 01:47:56 -0600163
164 // If destination exists...
Rob Landley7f184fa2008-02-21 04:44:42 -0600165
Rob Landley6e6871c2008-02-20 01:47:56 -0600166 } else {
167 if (S_ISDIR(st.st_mode)) TT.destisdir++;
168 else if (toys.optc > 1) goto error_notdir;
169 }
170
171 // Handle sources
Rob Landley7f184fa2008-02-21 04:44:42 -0600172
Rob Landley6e6871c2008-02-20 01:47:56 -0600173 for (i=0; i<toys.optc; i++) {
174 char *src = toys.optargs[i];
175
Rob Landley7f184fa2008-02-21 04:44:42 -0600176 // Skip nonexistent sources, or src==dest.
177
178 if (!strcmp(src, TT.destname)) continue;
179 if ((toys.optflags & FLAG_d) ? lstat(src, &st) : stat(src, &st))
180 {
Rob Landley6e6871c2008-02-20 01:47:56 -0600181 perror_msg("'%s'", src);
182 toys.exitval = 1;
183 continue;
184 }
185
186 // Copy directory or file.
Rob Landley7f184fa2008-02-21 04:44:42 -0600187
Rob Landley6e6871c2008-02-20 01:47:56 -0600188 if (S_ISDIR(st.st_mode)) {
189 if (toys.optflags & FLAG_r) {
Rob Landleyde695272008-02-24 03:48:06 -0600190 cp_file(src, &st, 0);
Rob Landley7f184fa2008-02-21 04:44:42 -0600191 strncpy(toybuf, src, sizeof(toybuf)-1);
192 toybuf[sizeof(toybuf)-1]=0;
193 dirtree_read(toybuf, NULL, cp_node);
Rob Landley6e6871c2008-02-20 01:47:56 -0600194 } else error_msg("Skipped dir '%s'", src);
Rob Landleyde695272008-02-24 03:48:06 -0600195 } else cp_file(src, &st, 0);
Rob Landley6e6871c2008-02-20 01:47:56 -0600196 }
Rob Landley7f184fa2008-02-21 04:44:42 -0600197
Rob Landley6e6871c2008-02-20 01:47:56 -0600198 return;
199
200error_notdir:
201 error_exit("'%s' isn't a directory", TT.destname);
202}