blob: 111f88d9f3beeb61855c448aa87611ff63955183 [file] [log] [blame]
Joe Onorato3ad977b2009-05-05 11:50:51 -07001#define LOG_TAG "file_backup_helper"
2
3#include <utils/backup_helpers.h>
4
5#include <utils/KeyedVector.h>
6#include <utils/ByteOrder.h>
7#include <utils/String8.h>
8
9#include <errno.h>
10#include <sys/types.h>
11#include <sys/uio.h>
12#include <sys/stat.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
Joe Onoratoc825d3e2009-05-06 12:55:46 -040016#include <utime.h>
Joe Onorato3ad977b2009-05-05 11:50:51 -070017#include <fcntl.h>
18#include <zlib.h>
19
20#include <cutils/log.h>
21
22using namespace android;
23
24#define MAGIC0 0x70616e53 // Snap
25#define MAGIC1 0x656c6946 // File
26
27struct SnapshotHeader {
28 int magic0;
29 int fileCount;
30 int magic1;
31 int totalSize;
32};
33
34struct FileState {
35 int modTime_sec;
36 int modTime_nsec;
37 int size;
38 int crc32;
39 int nameLen;
40};
41
42const static int ROUND_UP[4] = { 0, 3, 2, 1 };
43
44static inline int
45round_up(int n)
46{
47 return n + ROUND_UP[n % 4];
48}
49
50static int
51read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
52{
53 int bytesRead = 0;
54 int amt;
55 SnapshotHeader header;
56
57 amt = read(fd, &header, sizeof(header));
58 if (amt != sizeof(header)) {
59 return errno;
60 }
61 bytesRead += amt;
62
63 if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
64 LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
65 return 1;
66 }
67
68 for (int i=0; i<header.fileCount; i++) {
69 FileState file;
70 char filenameBuf[128];
71
72 amt = read(fd, &file, sizeof(file));
73 if (amt != sizeof(file)) {
74 LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
75 return 1;
76 }
77 bytesRead += amt;
78
79 // filename is not NULL terminated, but it is padded
80 int nameBufSize = round_up(file.nameLen);
81 char* filename = nameBufSize <= (int)sizeof(filenameBuf)
82 ? filenameBuf
83 : (char*)malloc(nameBufSize);
84 amt = read(fd, filename, nameBufSize);
85 if (amt == nameBufSize) {
86 snapshot->add(String8(filename, file.nameLen), file);
87 }
88 bytesRead += amt;
89 if (filename != filenameBuf) {
90 free(filename);
91 }
92 if (amt != nameBufSize) {
93 LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
94 return 1;
95 }
96 }
97
98 if (header.totalSize != bytesRead) {
99 LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
100 header.totalSize, bytesRead);
101 return 1;
102 }
103
104 return 0;
105}
106
107static int
108write_snapshot_file(int fd, const KeyedVector<String8,FileState>& snapshot)
109{
110 int bytesWritten = sizeof(SnapshotHeader);
111 // preflight size
112 const int N = snapshot.size();
113 for (int i=0; i<N; i++) {
114 const String8& name = snapshot.keyAt(i);
115 bytesWritten += sizeof(FileState) + round_up(name.length());
116 }
117
118 int amt;
119 SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten };
120
121 amt = write(fd, &header, sizeof(header));
122 if (amt != sizeof(header)) {
123 LOGW("write_snapshot_file error writing header %s", strerror(errno));
124 return errno;
125 }
126
127 for (int i=0; i<header.fileCount; i++) {
128 const String8& name = snapshot.keyAt(i);
129 FileState file = snapshot.valueAt(i);
130 int nameLen = file.nameLen = name.length();
131
132 amt = write(fd, &file, sizeof(file));
133 if (amt != sizeof(file)) {
134 LOGW("write_snapshot_file error writing header %s", strerror(errno));
135 return 1;
136 }
137
138 // filename is not NULL terminated, but it is padded
139 amt = write(fd, name.string(), nameLen);
140 if (amt != nameLen) {
141 LOGW("write_snapshot_file error writing filename %s", strerror(errno));
142 return 1;
143 }
144 int paddingLen = ROUND_UP[nameLen % 4];
145 if (paddingLen != 0) {
146 int padding = 0xabababab;
147 amt = write(fd, &padding, paddingLen);
148 if (amt != paddingLen) {
149 LOGW("write_snapshot_file error writing %d bytes of filename padding %s",
150 paddingLen, strerror(errno));
151 return 1;
152 }
153 }
154 }
155
156 return 0;
157}
158
159static int
160write_delete_file(const String8& key)
161{
162 printf("write_delete_file %s\n", key.string());
163 return 0;
164}
165
166static int
167write_update_file(const String8& realFilename, const String8& key)
168{
169 printf("write_update_file %s (%s)\n", realFilename.string(), key.string());
170 return 0;
171}
172
173static int
174compute_crc32(const String8& filename)
175{
176 const int bufsize = 4*1024;
177 int amt;
178
179 int fd = open(filename.string(), O_RDONLY);
180 if (fd == -1) {
181 return -1;
182 }
183
184 char* buf = (char*)malloc(bufsize);
185 int crc = crc32(0L, Z_NULL, 0);
186
187 while ((amt = read(fd, buf, bufsize)) != 0) {
188 crc = crc32(crc, (Bytef*)buf, amt);
189 }
190
191 close(fd);
192 free(buf);
193
194 return crc;
195}
196
197int
198back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream,
199 char const* fileBase, char const* const* files, int fileCount)
200{
201 int err;
202 const String8 base(fileBase);
203 KeyedVector<String8,FileState> oldSnapshot;
204 KeyedVector<String8,FileState> newSnapshot;
205
206 if (oldSnapshotFD != -1) {
207 err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
208 if (err != 0) {
209 // On an error, treat this as a full backup.
210 oldSnapshot.clear();
211 }
212 }
213
214 for (int i=0; i<fileCount; i++) {
215 String8 name(files[i]);
216 FileState s;
217 struct stat st;
218 String8 realFilename(base);
219 realFilename.appendPath(name);
220
221 err = stat(realFilename.string(), &st);
222 if (err != 0) {
223 LOGW("Error stating file %s", realFilename.string());
224 continue;
225 }
226
227 s.modTime_sec = st.st_mtime;
Joe Onoratoc825d3e2009-05-06 12:55:46 -0400228 s.modTime_nsec = 0; // workaround sim breakage
229 //s.modTime_nsec = st.st_mtime_nsec;
Joe Onorato3ad977b2009-05-05 11:50:51 -0700230 s.size = st.st_size;
231 s.crc32 = compute_crc32(realFilename);
232
233 newSnapshot.add(name, s);
234 }
235
236 int n = 0;
237 int N = oldSnapshot.size();
238 int m = 0;
239
240 while (n<N && m<fileCount) {
241 const String8& p = oldSnapshot.keyAt(n);
242 const String8& q = newSnapshot.keyAt(m);
243 int cmp = p.compare(q);
244 if (cmp > 0) {
245 // file added
246 String8 realFilename(base);
247 realFilename.appendPath(q);
248 write_update_file(realFilename, q);
249 m++;
250 }
251 else if (cmp < 0) {
252 // file removed
253 write_delete_file(p);
254 n++;
255 }
256 else {
257 // both files exist, check them
258 String8 realFilename(base);
259 realFilename.appendPath(q);
260 const FileState& f = oldSnapshot.valueAt(n);
261 const FileState& g = newSnapshot.valueAt(m);
262
263 printf("%s\n", q.string());
264 printf(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n",
265 f.modTime_sec, f.modTime_nsec, f.size, f.crc32);
266 printf(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n",
267 g.modTime_sec, g.modTime_nsec, g.size, g.crc32);
268 if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec
269 || f.size != g.size || f.crc32 != g.crc32) {
270 write_update_file(realFilename, p);
271 }
272 n++;
273 m++;
274 }
275 }
276
277 // these were deleted
278 while (n<N) {
279 write_delete_file(oldSnapshot.keyAt(n));
280 n++;
281 }
282
283 // these were added
284 while (m<fileCount) {
285 const String8& q = newSnapshot.keyAt(m);
286 String8 realFilename(base);
287 realFilename.appendPath(q);
288 write_update_file(realFilename, q);
289 m++;
290 }
291
292 err = write_snapshot_file(newSnapshotFD, newSnapshot);
293
294 return 0;
295}
296
297#if TEST_BACKUP_HELPERS
298
299#define SCRATCH_DIR "/data/backup_helper_test/"
300
301static int
302write_text_file(const char* path, const char* data)
303{
304 int amt;
305 int fd;
306 int len;
307
308 fd = creat(path, 0666);
309 if (fd == -1) {
310 fprintf(stderr, "creat %s failed\n", path);
311 return errno;
312 }
313
314 len = strlen(data);
315 amt = write(fd, data, len);
316 if (amt != len) {
317 fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
318 return errno;
319 }
320
321 close(fd);
322
323 return 0;
324}
325
326static int
327compare_file(const char* path, const unsigned char* data, int len)
328{
329 int fd;
330 int amt;
331
332 fd = open(path, O_RDONLY);
333 if (fd == -1) {
334 fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
335 return errno;
336 }
337
338 unsigned char* contents = (unsigned char*)malloc(len);
339 if (contents == NULL) {
340 fprintf(stderr, "malloc(%d) failed\n", len);
341 return ENOMEM;
342 }
343
344 bool sizesMatch = true;
345 amt = lseek(fd, 0, SEEK_END);
346 if (amt != len) {
347 fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
348 sizesMatch = false;
349 }
350 lseek(fd, 0, SEEK_SET);
351
352 int readLen = amt < len ? amt : len;
353 amt = read(fd, contents, readLen);
354 if (amt != readLen) {
355 fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
356 }
357
358 bool contentsMatch = true;
359 for (int i=0; i<readLen; i++) {
360 if (data[i] != contents[i]) {
361 if (contentsMatch) {
362 fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
363 contentsMatch = false;
364 }
365 fprintf(stderr, " [%-2d] %02x %02x\n", i, data[i], contents[i]);
366 }
367 }
368
369 return contentsMatch && sizesMatch ? 0 : 1;
370}
371
372int
373backup_helper_test_empty()
374{
375 int err;
376 int fd;
377 KeyedVector<String8,FileState> snapshot;
378 const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
379
380 system("rm -r " SCRATCH_DIR);
381 mkdir(SCRATCH_DIR, 0777);
382
383 // write
384 fd = creat(filename, 0666);
385 if (fd == -1) {
386 fprintf(stderr, "error creating %s\n", filename);
387 return 1;
388 }
389
390 err = write_snapshot_file(fd, snapshot);
391
392 close(fd);
393
394 if (err != 0) {
395 fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
396 return err;
397 }
398
399 static const unsigned char correct_data[] = {
400 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00,
401 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00
402 };
403
404 err = compare_file(filename, correct_data, sizeof(correct_data));
405 if (err != 0) {
406 return err;
407 }
408
409 // read
410 fd = open(filename, O_RDONLY);
411 if (fd == -1) {
412 fprintf(stderr, "error opening for read %s\n", filename);
413 return 1;
414 }
415
416 KeyedVector<String8,FileState> readSnapshot;
417 err = read_snapshot_file(fd, &readSnapshot);
418 if (err != 0) {
419 fprintf(stderr, "read_snapshot_file failed %d\n", err);
420 return err;
421 }
422
423 if (readSnapshot.size() != 0) {
424 fprintf(stderr, "readSnapshot should be length 0\n");
425 return 1;
426 }
427
428 return 0;
429}
430
431int
432backup_helper_test_four()
433{
434 int err;
435 int fd;
436 KeyedVector<String8,FileState> snapshot;
437 const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
438
439 system("rm -r " SCRATCH_DIR);
440 mkdir(SCRATCH_DIR, 0777);
441
442 // write
443 fd = creat(filename, 0666);
444 if (fd == -1) {
445 fprintf(stderr, "error opening %s\n", filename);
446 return 1;
447 }
448
449 String8 filenames[4];
450 FileState states[4];
451
452 states[0].modTime_sec = 0xfedcba98;
453 states[0].modTime_nsec = 0xdeadbeef;
454 states[0].size = 0xababbcbc;
455 states[0].crc32 = 0x12345678;
456 states[0].nameLen = -12;
457 filenames[0] = String8("bytes_of_padding");
458 snapshot.add(filenames[0], states[0]);
459
460 states[1].modTime_sec = 0x93400031;
461 states[1].modTime_nsec = 0xdeadbeef;
462 states[1].size = 0x88557766;
463 states[1].crc32 = 0x22334422;
464 states[1].nameLen = -1;
465 filenames[1] = String8("bytes_of_padding3");
466 snapshot.add(filenames[1], states[1]);
467
468 states[2].modTime_sec = 0x33221144;
469 states[2].modTime_nsec = 0xdeadbeef;
470 states[2].size = 0x11223344;
471 states[2].crc32 = 0x01122334;
472 states[2].nameLen = 0;
473 filenames[2] = String8("bytes_of_padding_2");
474 snapshot.add(filenames[2], states[2]);
475
476 states[3].modTime_sec = 0x33221144;
477 states[3].modTime_nsec = 0xdeadbeef;
478 states[3].size = 0x11223344;
479 states[3].crc32 = 0x01122334;
480 states[3].nameLen = 0;
481 filenames[3] = String8("bytes_of_padding__1");
482 snapshot.add(filenames[3], states[3]);
483
484 err = write_snapshot_file(fd, snapshot);
485
486 close(fd);
487
488 if (err != 0) {
489 fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
490 return err;
491 }
492
493 static const unsigned char correct_data[] = {
494 // header
495 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00,
496 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00,
497
498 // bytes_of_padding
499 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde,
500 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12,
501 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65,
502 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64,
503 0x64, 0x69, 0x6e, 0x67,
504
505 // bytes_of_padding3
506 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde,
507 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22,
508 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65,
509 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64,
510 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab,
511
512 // bytes of padding2
513 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde,
514 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01,
515 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65,
516 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64,
517 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab,
518
519 // bytes of padding3
520 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde,
521 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01,
522 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65,
523 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64,
524 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab
525 };
526
527 err = compare_file(filename, correct_data, sizeof(correct_data));
528 if (err != 0) {
529 return err;
530 }
531
532 // read
533 fd = open(filename, O_RDONLY);
534 if (fd == -1) {
535 fprintf(stderr, "error opening for read %s\n", filename);
536 return 1;
537 }
538
539
540 KeyedVector<String8,FileState> readSnapshot;
541 err = read_snapshot_file(fd, &readSnapshot);
542 if (err != 0) {
543 fprintf(stderr, "read_snapshot_file failed %d\n", err);
544 return err;
545 }
546
547 if (readSnapshot.size() != 4) {
548 fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
549 return 1;
550 }
551
552 bool matched = true;
553 for (size_t i=0; i<readSnapshot.size(); i++) {
554 const String8& name = readSnapshot.keyAt(i);
555 const FileState state = readSnapshot.valueAt(i);
556
557 if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
558 || states[i].modTime_nsec != state.modTime_nsec
559 || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
560 fprintf(stderr, "state %d expected={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n"
561 " actual={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n", i,
562 states[i].modTime_sec, states[i].modTime_nsec, states[i].size, states[i].crc32,
563 name.length(), filenames[i].string(),
564 state.modTime_sec, state.modTime_nsec, state.size, state.crc32, state.nameLen,
565 name.string());
566 matched = false;
567 }
568 }
569
570 return matched ? 0 : 1;
571}
572
573static int
574get_mod_time(const char* filename, struct timeval times[2])
575{
576 int err;
577 struct stat64 st;
578 err = stat64(filename, &st);
579 if (err != 0) {
580 fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
581 return errno;
582 }
583 times[0].tv_sec = st.st_atime;
584 times[0].tv_usec = st.st_atime_nsec / 1000;
585 times[1].tv_sec = st.st_mtime;
586 times[1].tv_usec = st.st_mtime_nsec / 1000;
587 return 0;
588}
589
590int
591backup_helper_test_files()
592{
593 int err;
594 int newSnapshotFD;
595 int oldSnapshotFD;
596
597 system("rm -r " SCRATCH_DIR);
598 mkdir(SCRATCH_DIR, 0777);
599 mkdir(SCRATCH_DIR "data", 0777);
600
601 write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
602 write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
603 write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
604 write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
605 write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
606 write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
607
608 char const* files_before[] = {
609 "data/b",
610 "data/c",
611 "data/d",
612 "data/e",
613 "data/f"
614 };
615
616 newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
617 if (newSnapshotFD == -1) {
618 fprintf(stderr, "error creating: %s\n", strerror(errno));
619 return errno;
620 }
621
622 err = back_up_files(-1, newSnapshotFD, 0, SCRATCH_DIR, files_before, 5);
623 if (err != 0) {
624 return err;
625 }
626
627 close(newSnapshotFD);
628
629 sleep(3);
630
631 struct timeval d_times[2];
632 struct timeval e_times[2];
633
634 err = get_mod_time(SCRATCH_DIR "data/d", d_times);
635 err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
636 if (err != 0) {
637 return err;
638 }
639
640 write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
641 unlink(SCRATCH_DIR "data/c");
642 write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
643 write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
644 utimes(SCRATCH_DIR "data/d", d_times);
645 write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
646 utimes(SCRATCH_DIR "data/e", e_times);
647 write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
648 unlink(SCRATCH_DIR "data/f");
649
650 char const* files_after[] = {
651 "data/a", // added
652 "data/b", // same
653 "data/c", // different mod time
654 "data/d", // different size (same mod time)
655 "data/e", // different contents (same mod time, same size)
656 "data/g" // added
657 };
658
659 oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
660 if (oldSnapshotFD == -1) {
661 fprintf(stderr, "error opening: %s\n", strerror(errno));
662 return errno;
663 }
664
665 newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
666 if (newSnapshotFD == -1) {
667 fprintf(stderr, "error creating: %s\n", strerror(errno));
668 return errno;
669 }
670
671 err = back_up_files(oldSnapshotFD, newSnapshotFD, 0, SCRATCH_DIR, files_after, 6);
672 if (err != 0) {
673 return err;
674 }
675
676 close(oldSnapshotFD);
677 close(newSnapshotFD);
678
679 return 0;
680}
681
682#endif // TEST_BACKUP_HELPERS