xref: /plan9/sys/src/cmd/fcp.c (revision 16a961bdb6a291dd2ca1e8266b5a1eba656ecc70)
1 #include <u.h>
2 #include <libc.h>
3 #define	DEFB	(8*1024)
4 #define	Nwork	16
5 
6 int	failed;
7 int	gflag;
8 int	uflag;
9 int	xflag;
10 void	copy(char *from, char *to, int todir);
11 int	copy1(int fdf, int fdt, char *from, char *to);
12 void	worker(int fdf, int fdt, char *from, char *to);
13 vlong	nextoff(void);
14 void	failure(void *, char *note);
15 
16 QLock	lk;
17 vlong	off;
18 
19 void
main(int argc,char * argv[])20 main(int argc, char *argv[])
21 {
22 	Dir *dirb;
23 	int todir, i;
24 
25 	ARGBEGIN {
26 	case 'g':
27 		gflag++;
28 		break;
29 	case 'u':
30 		uflag++;
31 		gflag++;
32 		break;
33 	case 'x':
34 		xflag++;
35 		break;
36 	default:
37 		goto usage;
38 	} ARGEND
39 
40 	todir=0;
41 	if(argc < 2)
42 		goto usage;
43 	dirb = dirstat(argv[argc-1]);
44 	if(dirb!=nil && (dirb->mode&DMDIR))
45 		todir=1;
46 	if(argc>2 && !todir){
47 		fprint(2, "fcp: %s not a directory\n", argv[argc-1]);
48 		exits("bad usage");
49 	}
50 	for(i=0; i<argc-1; i++)
51 		copy(argv[i], argv[argc-1], todir);
52 	if(failed)
53 		exits("errors");
54 	exits(0);
55 
56 usage:
57 	fprint(2, "usage:\tfcp [-gux] fromfile tofile\n");
58 	fprint(2, "\tfcp [-x] fromfile ... todir\n");
59 	exits("usage");
60 }
61 
62 int
samefile(Dir * a,char * an,char * bn)63 samefile(Dir *a, char *an, char *bn)
64 {
65 	Dir *b;
66 	int ret;
67 
68 	ret = 0;
69 	b=dirstat(bn);
70 	if(b != nil)
71 	if(b->qid.type==a->qid.type)
72 	if(b->qid.path==a->qid.path)
73 	if(b->qid.vers==a->qid.vers)
74 	if(b->dev==a->dev)
75 	if(b->type==a->type){
76 		fprint(2, "fcp: %s and %s are the same file\n", an, bn);
77 		ret = 1;
78 	}
79 	free(b);
80 	return ret;
81 }
82 
83 void
copy(char * from,char * to,int todir)84 copy(char *from, char *to, int todir)
85 {
86 	Dir *dirb, dirt;
87 	char name[256];
88 	int fdf, fdt, mode;
89 
90 	if(todir){
91 		char *s, *elem;
92 		elem=s=from;
93 		while(*s++)
94 			if(s[-1]=='/')
95 				elem=s;
96 		sprint(name, "%s/%s", to, elem);
97 		to=name;
98 	}
99 
100 	if((dirb=dirstat(from))==nil){
101 		fprint(2,"fcp: can't stat %s: %r\n", from);
102 		failed = 1;
103 		return;
104 	}
105 	mode = dirb->mode;
106 	if(mode&DMDIR){
107 		fprint(2, "fcp: %s is a directory\n", from);
108 		free(dirb);
109 		failed = 1;
110 		return;
111 	}
112 	if(samefile(dirb, from, to)){
113 		free(dirb);
114 		failed = 1;
115 		return;
116 	}
117 	mode &= 0777;
118 	fdf=open(from, OREAD);
119 	if(fdf<0){
120 		fprint(2, "fcp: can't open %s: %r\n", from);
121 		free(dirb);
122 		failed = 1;
123 		return;
124 	}
125 	fdt=create(to, OWRITE, mode);
126 	if(fdt<0){
127 		fprint(2, "fcp: can't create %s: %r\n", to);
128 		close(fdf);
129 		free(dirb);
130 		failed = 1;
131 		return;
132 	}
133 	if(copy1(fdf, fdt, from, to)==0 && (xflag || gflag || uflag)){
134 		nulldir(&dirt);
135 		if(xflag){
136 			dirt.mtime = dirb->mtime;
137 			dirt.mode = dirb->mode;
138 		}
139 		if(uflag)
140 			dirt.uid = dirb->uid;
141 		if(gflag)
142 			dirt.gid = dirb->gid;
143 		if(dirfwstat(fdt, &dirt) < 0)
144 			fprint(2, "fcp: warning: can't wstat %s: %r\n", to);
145 	}
146 	free(dirb);
147 	close(fdf);
148 	close(fdt);
149 }
150 
151 int
copy1(int fdf,int fdt,char * from,char * to)152 copy1(int fdf, int fdt, char *from, char *to)
153 {
154 	int i, n, rv, pid[Nwork];
155 	Waitmsg *w;
156 
157 	n = 0;
158 	off = 0;
159 	for(i=0; i<Nwork; i++){
160 		switch(pid[n] = rfork(RFPROC|RFMEM)){
161 		case 0:
162 			notify(failure);
163 			worker(fdf, fdt, from, to);
164 		case -1:
165 			break;
166 		default:
167 			n++;
168 			break;
169 		}
170 	}
171 	if(n == 0){
172 		fprint(2, "fcp: rfork: %r\n");
173 		failed = 1;
174 		return -1;
175 	}
176 
177 	rv = 0;
178 	while((w = wait()) != nil){
179 		if(w->msg[0]){
180 			rv = -1;
181 			failed = 1;
182 			for(i=0; i<n; i++)
183 				if(pid[i] > 0)
184 					postnote(PNPROC, pid[i], "failure");
185 		}
186 		free(w);
187 	}
188 	return rv;
189 }
190 
191 void
worker(int fdf,int fdt,char * from,char * to)192 worker(int fdf, int fdt, char *from, char *to)
193 {
194 	char buf[DEFB], *bp;
195 	long len, n;
196 	vlong o;
197 
198 	len = sizeof(buf);
199 	bp = buf;
200 	o = nextoff();
201 
202 	while(n = pread(fdf, bp, len, o)){
203 		if(n < 0){
204 			fprint(2, "reading %s at %lld: %r\n", from, o);
205 			_exits("bad");
206 		}
207 		if(pwrite(fdt, buf, n, o) != n){
208 			fprint(2, "writing %s: %r\n", to);
209 			_exits("bad");
210 		}
211 		bp += n;
212 		o += n;
213 		len -= n;
214 		if(len == 0){
215 			len = sizeof buf;
216 			bp = buf;
217 			o = nextoff();
218 		}
219 	}
220 	_exits(nil);
221 }
222 
223 vlong
nextoff(void)224 nextoff(void)
225 {
226 	vlong o;
227 
228 	qlock(&lk);
229 	o = off;
230 	off += DEFB;
231 	qunlock(&lk);
232 
233 	return o;
234 }
235 
236 void
failure(void *,char * note)237 failure(void*, char *note)
238 {
239 	if(strcmp(note, "failure") == 0)
240 		_exits(nil);
241 	noted(NDFLT);
242 }
243