diff options
Diffstat (limited to 'adb/file_sync_service.c')
-rw-r--r-- | adb/file_sync_service.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c new file mode 100644 index 000000000..21f8af775 --- /dev/null +++ b/adb/file_sync_service.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 The Android Open Source Project | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | #include <stdlib.h> | ||
18 | #include <stdio.h> | ||
19 | #include <string.h> | ||
20 | |||
21 | #include <sys/stat.h> | ||
22 | #include <sys/types.h> | ||
23 | #include <dirent.h> | ||
24 | #include <utime.h> | ||
25 | |||
26 | #include <errno.h> | ||
27 | |||
28 | #include "sysdeps.h" | ||
29 | |||
30 | #define TRACE_TAG TRACE_SYNC | ||
31 | #include "adb.h" | ||
32 | #include "file_sync_service.h" | ||
33 | |||
34 | static int mkdirs(char *name) | ||
35 | { | ||
36 | int ret; | ||
37 | char *x = name + 1; | ||
38 | |||
39 | if(name[0] != '/') return -1; | ||
40 | |||
41 | for(;;) { | ||
42 | x = adb_dirstart(x); | ||
43 | if(x == 0) return 0; | ||
44 | *x = 0; | ||
45 | ret = adb_mkdir(name, 0775); | ||
46 | if((ret < 0) && (errno != EEXIST)) { | ||
47 | D("mkdir(\"%s\") -> %s\n", name, strerror(errno)); | ||
48 | *x = '/'; | ||
49 | return ret; | ||
50 | } | ||
51 | *x++ = '/'; | ||
52 | } | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static int do_stat(int s, const char *path) | ||
57 | { | ||
58 | syncmsg msg; | ||
59 | struct stat st; | ||
60 | |||
61 | msg.stat.id = ID_STAT; | ||
62 | |||
63 | if(lstat(path, &st)) { | ||
64 | msg.stat.mode = 0; | ||
65 | msg.stat.size = 0; | ||
66 | msg.stat.time = 0; | ||
67 | } else { | ||
68 | msg.stat.mode = htoll(st.st_mode); | ||
69 | msg.stat.size = htoll(st.st_size); | ||
70 | msg.stat.time = htoll(st.st_mtime); | ||
71 | } | ||
72 | |||
73 | return writex(s, &msg.stat, sizeof(msg.stat)); | ||
74 | } | ||
75 | |||
76 | static int do_list(int s, const char *path) | ||
77 | { | ||
78 | DIR *d; | ||
79 | struct dirent *de; | ||
80 | struct stat st; | ||
81 | syncmsg msg; | ||
82 | int len; | ||
83 | |||
84 | char tmp[1024 + 256 + 1]; | ||
85 | char *fname; | ||
86 | |||
87 | len = strlen(path); | ||
88 | memcpy(tmp, path, len); | ||
89 | tmp[len] = '/'; | ||
90 | fname = tmp + len + 1; | ||
91 | |||
92 | msg.dent.id = ID_DENT; | ||
93 | |||
94 | d = opendir(path); | ||
95 | if(d == 0) goto done; | ||
96 | |||
97 | while((de = readdir(d))) { | ||
98 | int len = strlen(de->d_name); | ||
99 | |||
100 | /* not supposed to be possible, but | ||
101 | if it does happen, let's not buffer overrun */ | ||
102 | if(len > 256) continue; | ||
103 | |||
104 | strcpy(fname, de->d_name); | ||
105 | if(lstat(tmp, &st) == 0) { | ||
106 | msg.dent.mode = htoll(st.st_mode); | ||
107 | msg.dent.size = htoll(st.st_size); | ||
108 | msg.dent.time = htoll(st.st_mtime); | ||
109 | msg.dent.namelen = htoll(len); | ||
110 | |||
111 | if(writex(s, &msg.dent, sizeof(msg.dent)) || | ||
112 | writex(s, de->d_name, len)) { | ||
113 | return -1; | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | closedir(d); | ||
119 | |||
120 | done: | ||
121 | msg.dent.id = ID_DONE; | ||
122 | msg.dent.mode = 0; | ||
123 | msg.dent.size = 0; | ||
124 | msg.dent.time = 0; | ||
125 | msg.dent.namelen = 0; | ||
126 | return writex(s, &msg.dent, sizeof(msg.dent)); | ||
127 | } | ||
128 | |||
129 | static int fail_message(int s, const char *reason) | ||
130 | { | ||
131 | syncmsg msg; | ||
132 | int len = strlen(reason); | ||
133 | |||
134 | D("sync: failure: %s\n", reason); | ||
135 | |||
136 | msg.data.id = ID_FAIL; | ||
137 | msg.data.size = htoll(len); | ||
138 | if(writex(s, &msg.data, sizeof(msg.data)) || | ||
139 | writex(s, reason, len)) { | ||
140 | return -1; | ||
141 | } else { | ||
142 | return 0; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | static int fail_errno(int s) | ||
147 | { | ||
148 | return fail_message(s, strerror(errno)); | ||
149 | } | ||
150 | |||
151 | static int handle_send_file(int s, char *path, mode_t mode, char *buffer) | ||
152 | { | ||
153 | syncmsg msg; | ||
154 | unsigned int timestamp = 0; | ||
155 | int fd; | ||
156 | |||
157 | fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); | ||
158 | if(fd < 0 && errno == ENOENT) { | ||
159 | mkdirs(path); | ||
160 | fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); | ||
161 | } | ||
162 | if(fd < 0 && errno == EEXIST) { | ||
163 | fd = adb_open_mode(path, O_WRONLY, mode); | ||
164 | } | ||
165 | if(fd < 0) { | ||
166 | if(fail_errno(s)) | ||
167 | return -1; | ||
168 | fd = -1; | ||
169 | } | ||
170 | |||
171 | for(;;) { | ||
172 | unsigned int len; | ||
173 | |||
174 | if(readx(s, &msg.data, sizeof(msg.data))) | ||
175 | goto fail; | ||
176 | |||
177 | if(msg.data.id != ID_DATA) { | ||
178 | if(msg.data.id == ID_DONE) { | ||
179 | timestamp = ltohl(msg.data.size); | ||
180 | break; | ||
181 | } | ||
182 | fail_message(s, "invalid data message"); | ||
183 | goto fail; | ||
184 | } | ||
185 | len = ltohl(msg.data.size); | ||
186 | if(len > SYNC_DATA_MAX) { | ||
187 | fail_message(s, "oversize data message"); | ||
188 | goto fail; | ||
189 | } | ||
190 | if(readx(s, buffer, len)) | ||
191 | goto fail; | ||
192 | |||
193 | if(fd < 0) | ||
194 | continue; | ||
195 | if(writex(fd, buffer, len)) { | ||
196 | adb_close(fd); | ||
197 | adb_unlink(path); | ||
198 | fd = -1; | ||
199 | if(fail_errno(s)) return -1; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | if(fd >= 0) { | ||
204 | struct utimbuf u; | ||
205 | adb_close(fd); | ||
206 | u.actime = timestamp; | ||
207 | u.modtime = timestamp; | ||
208 | utime(path, &u); | ||
209 | |||
210 | msg.status.id = ID_OKAY; | ||
211 | msg.status.msglen = 0; | ||
212 | if(writex(s, &msg.status, sizeof(msg.status))) | ||
213 | return -1; | ||
214 | } | ||
215 | return 0; | ||
216 | |||
217 | fail: | ||
218 | if(fd >= 0) | ||
219 | adb_close(fd); | ||
220 | adb_unlink(path); | ||
221 | return -1; | ||
222 | } | ||
223 | |||
224 | #ifdef HAVE_SYMLINKS | ||
225 | static int handle_send_link(int s, char *path, char *buffer) | ||
226 | { | ||
227 | syncmsg msg; | ||
228 | unsigned int len; | ||
229 | int ret; | ||
230 | |||
231 | if(readx(s, &msg.data, sizeof(msg.data))) | ||
232 | return -1; | ||
233 | |||
234 | if(msg.data.id != ID_DATA) { | ||
235 | fail_message(s, "invalid data message: expected ID_DATA"); | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | len = ltohl(msg.data.size); | ||
240 | if(len > SYNC_DATA_MAX) { | ||
241 | fail_message(s, "oversize data message"); | ||
242 | return -1; | ||
243 | } | ||
244 | if(readx(s, buffer, len)) | ||
245 | return -1; | ||
246 | |||
247 | ret = symlink(buffer, path); | ||
248 | if(ret && errno == ENOENT) { | ||
249 | mkdirs(path); | ||
250 | ret = symlink(buffer, path); | ||
251 | } | ||
252 | if(ret) { | ||
253 | fail_errno(s); | ||
254 | return -1; | ||
255 | } | ||
256 | |||
257 | if(readx(s, &msg.data, sizeof(msg.data))) | ||
258 | return -1; | ||
259 | |||
260 | if(msg.data.id == ID_DONE) { | ||
261 | msg.status.id = ID_OKAY; | ||
262 | msg.status.msglen = 0; | ||
263 | if(writex(s, &msg.status, sizeof(msg.status))) | ||
264 | return -1; | ||
265 | } else { | ||
266 | fail_message(s, "invalid data message: expected ID_DONE"); | ||
267 | return -1; | ||
268 | } | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | #endif /* HAVE_SYMLINKS */ | ||
273 | |||
274 | static int do_send(int s, char *path, char *buffer) | ||
275 | { | ||
276 | char *tmp; | ||
277 | mode_t mode; | ||
278 | int is_link, ret; | ||
279 | |||
280 | tmp = strrchr(path,','); | ||
281 | if(tmp) { | ||
282 | *tmp = 0; | ||
283 | errno = 0; | ||
284 | mode = strtoul(tmp + 1, NULL, 0); | ||
285 | #ifndef HAVE_SYMLINKS | ||
286 | is_link = 0; | ||
287 | #else | ||
288 | is_link = S_ISLNK(mode); | ||
289 | #endif | ||
290 | mode &= 0777; | ||
291 | } | ||
292 | if(!tmp || errno) { | ||
293 | mode = 0644; | ||
294 | is_link = 0; | ||
295 | } | ||
296 | |||
297 | adb_unlink(path); | ||
298 | |||
299 | |||
300 | #ifdef HAVE_SYMLINKS | ||
301 | if(is_link) | ||
302 | ret = handle_send_link(s, path, buffer); | ||
303 | else { | ||
304 | #else | ||
305 | { | ||
306 | #endif | ||
307 | /* copy user permission bits to "group" and "other" permissions */ | ||
308 | mode |= ((mode >> 3) & 0070); | ||
309 | mode |= ((mode >> 3) & 0007); | ||
310 | |||
311 | ret = handle_send_file(s, path, mode, buffer); | ||
312 | } | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | static int do_recv(int s, const char *path, char *buffer) | ||
318 | { | ||
319 | syncmsg msg; | ||
320 | int fd, r; | ||
321 | |||
322 | fd = adb_open(path, O_RDONLY); | ||
323 | if(fd < 0) { | ||
324 | if(fail_errno(s)) return -1; | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | msg.data.id = ID_DATA; | ||
329 | for(;;) { | ||
330 | r = adb_read(fd, buffer, SYNC_DATA_MAX); | ||
331 | if(r <= 0) { | ||
332 | if(r == 0) break; | ||
333 | if(errno == EINTR) continue; | ||
334 | r = fail_errno(s); | ||
335 | adb_close(fd); | ||
336 | return r; | ||
337 | } | ||
338 | msg.data.size = htoll(r); | ||
339 | if(writex(s, &msg.data, sizeof(msg.data)) || | ||
340 | writex(s, buffer, r)) { | ||
341 | adb_close(fd); | ||
342 | return -1; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | adb_close(fd); | ||
347 | |||
348 | msg.data.id = ID_DONE; | ||
349 | msg.data.size = 0; | ||
350 | if(writex(s, &msg.data, sizeof(msg.data))) { | ||
351 | return -1; | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | void file_sync_service(int fd, void *cookie) | ||
358 | { | ||
359 | syncmsg msg; | ||
360 | char name[1025]; | ||
361 | unsigned namelen; | ||
362 | |||
363 | char *buffer = malloc(SYNC_DATA_MAX); | ||
364 | if(buffer == 0) goto fail; | ||
365 | |||
366 | for(;;) { | ||
367 | D("sync: waiting for command\n"); | ||
368 | |||
369 | if(readx(fd, &msg.req, sizeof(msg.req))) { | ||
370 | fail_message(fd, "command read failure"); | ||
371 | break; | ||
372 | } | ||
373 | namelen = ltohl(msg.req.namelen); | ||
374 | if(namelen > 1024) { | ||
375 | fail_message(fd, "invalid namelen"); | ||
376 | break; | ||
377 | } | ||
378 | if(readx(fd, name, namelen)) { | ||
379 | fail_message(fd, "filename read failure"); | ||
380 | break; | ||
381 | } | ||
382 | name[namelen] = 0; | ||
383 | |||
384 | msg.req.namelen = 0; | ||
385 | D("sync: '%s' '%s'\n", (char*) &msg.req, name); | ||
386 | |||
387 | switch(msg.req.id) { | ||
388 | case ID_STAT: | ||
389 | if(do_stat(fd, name)) goto fail; | ||
390 | break; | ||
391 | case ID_LIST: | ||
392 | if(do_list(fd, name)) goto fail; | ||
393 | break; | ||
394 | case ID_SEND: | ||
395 | if(do_send(fd, name, buffer)) goto fail; | ||
396 | break; | ||
397 | case ID_RECV: | ||
398 | if(do_recv(fd, name, buffer)) goto fail; | ||
399 | break; | ||
400 | case ID_QUIT: | ||
401 | goto fail; | ||
402 | default: | ||
403 | fail_message(fd, "unknown command"); | ||
404 | goto fail; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | fail: | ||
409 | if(buffer != 0) free(buffer); | ||
410 | D("sync: done\n"); | ||
411 | adb_close(fd); | ||
412 | } | ||