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