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