blob: 4ad9b5179532a7b08d9bae10d8824e13d666e131 [file] [log] [blame]
Jean-Baptiste Queru4d3b5c12009-07-21 11:16:54 -07001/*
2 * Copyright (C) 2009 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#define LOG_TAG "file_backup_helper"
18
19#include <utils/BackupHelpers.h>
20
21#include <utils/KeyedVector.h>
22#include <utils/ByteOrder.h>
23#include <utils/String8.h>
24
25#include <errno.h>
26#include <sys/types.h>
27#include <sys/uio.h>
28#include <sys/stat.h>
29#include <sys/time.h> // for utimes
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <utime.h>
34#include <fcntl.h>
35#include <zlib.h>
36
37#include <cutils/log.h>
38
39namespace android {
40
41#define MAGIC0 0x70616e53 // Snap
42#define MAGIC1 0x656c6946 // File
43
44/*
45 * File entity data format (v1):
46 *
47 * - 4-byte version number of the metadata, little endian (0x00000001 for v1)
48 * - 12 bytes of metadata
49 * - the file data itself
50 *
51 * i.e. a 16-byte metadata header followed by the raw file data. If the
52 * restore code does not recognize the metadata version, it can still
53 * interpret the file data itself correctly.
54 *
55 * file_metadata_v1:
56 *
57 * - 4 byte version number === 0x00000001 (little endian)
58 * - 4-byte access mode (little-endian)
59 * - undefined (8 bytes)
60 */
61
62struct file_metadata_v1 {
63 int version;
64 int mode;
65 int undefined_1;
66 int undefined_2;
67};
68
69const static int CURRENT_METADATA_VERSION = 1;
70
71#if 1
72#define LOGP(f, x...)
73#else
74#if TEST_BACKUP_HELPERS
75#define LOGP(f, x...) printf(f "\n", x)
76#else
77#define LOGP(x...) LOGD(x)
78#endif
79#endif
80
81const static int ROUND_UP[4] = { 0, 3, 2, 1 };
82
83static inline int
84round_up(int n)
85{
86 return n + ROUND_UP[n % 4];
87}
88
89static int
90read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
91{
92 int bytesRead = 0;
93 int amt;
94 SnapshotHeader header;
95
96 amt = read(fd, &header, sizeof(header));
97 if (amt != sizeof(header)) {
98 return errno;
99 }
100 bytesRead += amt;
101
102 if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
103 LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
104 return 1;
105 }
106
107 for (int i=0; i<header.fileCount; i++) {
108 FileState file;
109 char filenameBuf[128];
110
111 amt = read(fd, &file, sizeof(FileState));
112 if (amt != sizeof(FileState)) {
113 LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
114 return 1;
115 }
116 bytesRead += amt;
117
118 // filename is not NULL terminated, but it is padded
119 int nameBufSize = round_up(file.nameLen);
120 char* filename = nameBufSize <= (int)sizeof(filenameBuf)
121 ? filenameBuf
122 : (char*)malloc(nameBufSize);
123 amt = read(fd, filename, nameBufSize);
124 if (amt == nameBufSize) {
125 snapshot->add(String8(filename, file.nameLen), file);
126 }
127 bytesRead += amt;
128 if (filename != filenameBuf) {
129 free(filename);
130 }
131 if (amt != nameBufSize) {
132 LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
133 return 1;
134 }
135 }
136
137 if (header.totalSize != bytesRead) {
138 LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
139 header.totalSize, bytesRead);
140 return 1;
141 }
142
143 return 0;
144}
145
146static int
147write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot)
148{
149 int fileCount = 0;
150 int bytesWritten = sizeof(SnapshotHeader);
151 // preflight size
152 const int N = snapshot.size();
153 for (int i=0; i<N; i++) {
154 const FileRec& g = snapshot.valueAt(i);
155 if (!g.deleted) {
156 const String8& name = snapshot.keyAt(i);
157 bytesWritten += sizeof(FileState) + round_up(name.length());
158 fileCount++;
159 }
160 }
161
162 LOGP("write_snapshot_file fd=%d\n", fd);
163
164 int amt;
165 SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten };
166
167 amt = write(fd, &header, sizeof(header));
168 if (amt != sizeof(header)) {
169 LOGW("write_snapshot_file error writing header %s", strerror(errno));
170 return errno;
171 }
172
173 for (int i=0; i<N; i++) {
174 FileRec r = snapshot.valueAt(i);
175 if (!r.deleted) {
176 const String8& name = snapshot.keyAt(i);
177 int nameLen = r.s.nameLen = name.length();
178
179 amt = write(fd, &r.s, sizeof(FileState));
180 if (amt != sizeof(FileState)) {
181 LOGW("write_snapshot_file error writing header %s", strerror(errno));
182 return 1;
183 }
184
185 // filename is not NULL terminated, but it is padded
186 amt = write(fd, name.string(), nameLen);
187 if (amt != nameLen) {
188 LOGW("write_snapshot_file error writing filename %s", strerror(errno));
189 return 1;
190 }
191 int paddingLen = ROUND_UP[nameLen % 4];
192 if (paddingLen != 0) {
193 int padding = 0xabababab;
194 amt = write(fd, &padding, paddingLen);
195 if (amt != paddingLen) {
196 LOGW("write_snapshot_file error writing %d bytes of filename padding %s",
197 paddingLen, strerror(errno));
198 return 1;
199 }
200 }
201 }
202 }
203
204 return 0;
205}
206
207static int
208write_delete_file(BackupDataWriter* dataStream, const String8& key)
209{
210 LOGP("write_delete_file %s\n", key.string());
211 return dataStream->WriteEntityHeader(key, -1);
212}
213
214static int
215write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key,
216 char const* realFilename)
217{
218 LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode);
219
220 const int bufsize = 4*1024;
221 int err;
222 int amt;
223 int fileSize;
224 int bytesLeft;
225 file_metadata_v1 metadata;
226
227 char* buf = (char*)malloc(bufsize);
228 int crc = crc32(0L, Z_NULL, 0);
229
230
231 fileSize = lseek(fd, 0, SEEK_END);
232 lseek(fd, 0, SEEK_SET);
233
234 if (sizeof(metadata) != 16) {
235 LOGE("ERROR: metadata block is the wrong size!");
236 }
237
238 bytesLeft = fileSize + sizeof(metadata);
239 err = dataStream->WriteEntityHeader(key, bytesLeft);
240 if (err != 0) {
241 free(buf);
242 return err;
243 }
244
245 // store the file metadata first
246 metadata.version = tolel(CURRENT_METADATA_VERSION);
247 metadata.mode = tolel(mode);
248 metadata.undefined_1 = metadata.undefined_2 = 0;
249 err = dataStream->WriteEntityData(&metadata, sizeof(metadata));
250 if (err != 0) {
251 free(buf);
252 return err;
253 }
254 bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now
255
256 // now store the file content
257 while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) {
258 bytesLeft -= amt;
259 if (bytesLeft < 0) {
260 amt += bytesLeft; // Plus a negative is minus. Don't write more than we promised.
261 }
262 err = dataStream->WriteEntityData(buf, amt);
263 if (err != 0) {
264 free(buf);
265 return err;
266 }
267 }
268 if (bytesLeft != 0) {
269 if (bytesLeft > 0) {
270 // Pad out the space we promised in the buffer. We can't corrupt the buffer,
271 // even though the data we're sending is probably bad.
272 memset(buf, 0, bufsize);
273 while (bytesLeft > 0) {
274 amt = bytesLeft < bufsize ? bytesLeft : bufsize;
275 bytesLeft -= amt;
276 err = dataStream->WriteEntityData(buf, amt);
277 if (err != 0) {
278 free(buf);
279 return err;
280 }
281 }
282 }
283 LOGE("write_update_file size mismatch for %s. expected=%d actual=%d."
284 " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft);
285 }
286
287 free(buf);
288 return NO_ERROR;
289}
290
291static int
292write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename)
293{
294 int err;
295 struct stat st;
296
297 err = stat(realFilename, &st);
298 if (err < 0) {
299 return errno;
300 }
301
302 int fd = open(realFilename, O_RDONLY);
303 if (fd == -1) {
304 return errno;
305 }
306
307 err = write_update_file(dataStream, fd, st.st_mode, key, realFilename);
308 close(fd);
309 return err;
310}
311
312static int
313compute_crc32(int fd)
314{
315 const int bufsize = 4*1024;
316 int amt;
317
318 char* buf = (char*)malloc(bufsize);
319 int crc = crc32(0L, Z_NULL, 0);
320
321 lseek(fd, 0, SEEK_SET);
322
323 while ((amt = read(fd, buf, bufsize)) != 0) {
324 crc = crc32(crc, (Bytef*)buf, amt);
325 }
326
327 free(buf);
328 return crc;
329}
330
331int
332back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
333 char const* const* files, char const* const* keys, int fileCount)
334{
335 int err;
336 KeyedVector<String8,FileState> oldSnapshot;
337 KeyedVector<String8,FileRec> newSnapshot;
338
339 if (oldSnapshotFD != -1) {
340 err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
341 if (err != 0) {
342 // On an error, treat this as a full backup.
343 oldSnapshot.clear();
344 }
345 }
346
347 for (int i=0; i<fileCount; i++) {
348 String8 key(keys[i]);
349 FileRec r;
350 char const* file = files[i];
351 r.file = file;
352 struct stat st;
353
354 err = stat(file, &st);
355 if (err != 0) {
356 r.deleted = true;
357 } else {
358 r.deleted = false;
359 r.s.modTime_sec = st.st_mtime;
360 r.s.modTime_nsec = 0; // workaround sim breakage
361 //r.s.modTime_nsec = st.st_mtime_nsec;
362 r.s.mode = st.st_mode;
363 r.s.size = st.st_size;
364 // we compute the crc32 later down below, when we already have the file open.
365
366 if (newSnapshot.indexOfKey(key) >= 0) {
367 LOGP("back_up_files key already in use '%s'", key.string());
368 return -1;
369 }
370 }
371 newSnapshot.add(key, r);
372 }
373
374 int n = 0;
375 int N = oldSnapshot.size();
376 int m = 0;
377
378 while (n<N && m<fileCount) {
379 const String8& p = oldSnapshot.keyAt(n);
380 const String8& q = newSnapshot.keyAt(m);
381 FileRec& g = newSnapshot.editValueAt(m);
382 int cmp = p.compare(q);
383 if (g.deleted || cmp < 0) {
384 // file removed
385 LOGP("file removed: %s", p.string());
386 g.deleted = true; // They didn't mention the file, but we noticed that it's gone.
387 dataStream->WriteEntityHeader(p, -1);
388 n++;
389 }
390 else if (cmp > 0) {
391 // file added
392 LOGP("file added: %s", g.file.string());
393 write_update_file(dataStream, q, g.file.string());
394 m++;
395 }
396 else {
397 // both files exist, check them
398 const FileState& f = oldSnapshot.valueAt(n);
399
400 int fd = open(g.file.string(), O_RDONLY);
401 if (fd < 0) {
402 // We can't open the file. Don't report it as a delete either. Let the
403 // server keep the old version. Maybe they'll be able to deal with it
404 // on restore.
405 LOGP("Unable to open file %s - skipping", g.file.string());
406 } else {
407 g.s.crc32 = compute_crc32(fd);
408
409 LOGP("%s", q.string());
410 LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
411 f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32);
412 LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
413 g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32);
414 if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
415 || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) {
416 write_update_file(dataStream, fd, g.s.mode, p, g.file.string());
417 }
418
419 close(fd);
420 }
421 n++;
422 m++;
423 }
424 }
425
426 // these were deleted
427 while (n<N) {
428 dataStream->WriteEntityHeader(oldSnapshot.keyAt(n), -1);
429 n++;
430 }
431
432 // these were added
433 while (m<fileCount) {
434 const String8& q = newSnapshot.keyAt(m);
435 FileRec& g = newSnapshot.editValueAt(m);
436 write_update_file(dataStream, q, g.file.string());
437 m++;
438 }
439
440 err = write_snapshot_file(newSnapshotFD, newSnapshot);
441
442 return 0;
443}
444
445#define RESTORE_BUF_SIZE (8*1024)
446
447RestoreHelperBase::RestoreHelperBase()
448{
449 m_buf = malloc(RESTORE_BUF_SIZE);
450 m_loggedUnknownMetadata = false;
451}
452
453RestoreHelperBase::~RestoreHelperBase()
454{
455 free(m_buf);
456}
457
458status_t
459RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
460{
461 ssize_t err;
462 size_t dataSize;
463 String8 key;
464 int fd;
465 void* buf = m_buf;
466 ssize_t amt;
467 int mode;
468 int crc;
469 struct stat st;
470 FileRec r;
471
472 err = in->ReadEntityHeader(&key, &dataSize);
473 if (err != NO_ERROR) {
474 return err;
475 }
476
477 // Get the metadata block off the head of the file entity and use that to
478 // set up the output file
479 file_metadata_v1 metadata;
480 amt = in->ReadEntityData(&metadata, sizeof(metadata));
481 if (amt != sizeof(metadata)) {
482 LOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
483 (long)amt, strerror(errno));
484 return EIO;
485 }
486 metadata.version = fromlel(metadata.version);
487 metadata.mode = fromlel(metadata.mode);
488 if (metadata.version > CURRENT_METADATA_VERSION) {
489 if (!m_loggedUnknownMetadata) {
490 m_loggedUnknownMetadata = true;
491 LOGW("Restoring file with unsupported metadata version %d (currently %d)",
492 metadata.version, CURRENT_METADATA_VERSION);
493 }
494 }
495 mode = metadata.mode;
496
497 // Write the file and compute the crc
498 crc = crc32(0L, Z_NULL, 0);
499 fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
500 if (fd == -1) {
501 LOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
502 return errno;
503 }
504
505 while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
506 err = write(fd, buf, amt);
507 if (err != amt) {
508 close(fd);
509 LOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
510 return errno;
511 }
512 crc = crc32(crc, (Bytef*)buf, amt);
513 }
514
515 close(fd);
516
517 // Record for the snapshot
518 err = stat(filename.string(), &st);
519 if (err != 0) {
520 LOGW("Error stating file that we just created %s", filename.string());
521 return errno;
522 }
523
524 r.file = filename;
525 r.deleted = false;
526 r.s.modTime_sec = st.st_mtime;
527 r.s.modTime_nsec = 0; // workaround sim breakage
528 //r.s.modTime_nsec = st.st_mtime_nsec;
529 r.s.mode = st.st_mode;
530 r.s.size = st.st_size;
531 r.s.crc32 = crc;
532
533 m_files.add(key, r);
534
535 return NO_ERROR;
536}
537
538status_t
539RestoreHelperBase::WriteSnapshot(int fd)
540{
541 return write_snapshot_file(fd, m_files);;
542}
543
544#if TEST_BACKUP_HELPERS
545
546#define SCRATCH_DIR "/data/backup_helper_test/"
547
548static int
549write_text_file(const char* path, const char* data)
550{
551 int amt;
552 int fd;
553 int len;
554
555 fd = creat(path, 0666);
556 if (fd == -1) {
557 fprintf(stderr, "creat %s failed\n", path);
558 return errno;
559 }
560
561 len = strlen(data);
562 amt = write(fd, data, len);
563 if (amt != len) {
564 fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
565 return errno;
566 }
567
568 close(fd);
569
570 return 0;
571}
572
573static int
574compare_file(const char* path, const unsigned char* data, int len)
575{
576 int fd;
577 int amt;
578
579 fd = open(path, O_RDONLY);
580 if (fd == -1) {
581 fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
582 return errno;
583 }
584
585 unsigned char* contents = (unsigned char*)malloc(len);
586 if (contents == NULL) {
587 fprintf(stderr, "malloc(%d) failed\n", len);
588 return ENOMEM;
589 }
590
591 bool sizesMatch = true;
592 amt = lseek(fd, 0, SEEK_END);
593 if (amt != len) {
594 fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
595 sizesMatch = false;
596 }
597 lseek(fd, 0, SEEK_SET);
598
599 int readLen = amt < len ? amt : len;
600 amt = read(fd, contents, readLen);
601 if (amt != readLen) {
602 fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
603 }
604
605 bool contentsMatch = true;
606 for (int i=0; i<readLen; i++) {
607 if (data[i] != contents[i]) {
608 if (contentsMatch) {
609 fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
610 contentsMatch = false;
611 }
612 fprintf(stderr, " [%-2d] %02x %02x\n", i, data[i], contents[i]);
613 }
614 }
615
616 free(contents);
617 return contentsMatch && sizesMatch ? 0 : 1;
618}
619
620int
621backup_helper_test_empty()
622{
623 int err;
624 int fd;
625 KeyedVector<String8,FileRec> snapshot;
626 const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
627
628 system("rm -r " SCRATCH_DIR);
629 mkdir(SCRATCH_DIR, 0777);
630
631 // write
632 fd = creat(filename, 0666);
633 if (fd == -1) {
634 fprintf(stderr, "error creating %s\n", filename);
635 return 1;
636 }
637
638 err = write_snapshot_file(fd, snapshot);
639
640 close(fd);
641
642 if (err != 0) {
643 fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
644 return err;
645 }
646
647 static const unsigned char correct_data[] = {
648 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00,
649 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00
650 };
651
652 err = compare_file(filename, correct_data, sizeof(correct_data));
653 if (err != 0) {
654 return err;
655 }
656
657 // read
658 fd = open(filename, O_RDONLY);
659 if (fd == -1) {
660 fprintf(stderr, "error opening for read %s\n", filename);
661 return 1;
662 }
663
664 KeyedVector<String8,FileState> readSnapshot;
665 err = read_snapshot_file(fd, &readSnapshot);
666 if (err != 0) {
667 fprintf(stderr, "read_snapshot_file failed %d\n", err);
668 return err;
669 }
670
671 if (readSnapshot.size() != 0) {
672 fprintf(stderr, "readSnapshot should be length 0\n");
673 return 1;
674 }
675
676 return 0;
677}
678
679int
680backup_helper_test_four()
681{
682 int err;
683 int fd;
684 KeyedVector<String8,FileRec> snapshot;
685 const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
686
687 system("rm -r " SCRATCH_DIR);
688 mkdir(SCRATCH_DIR, 0777);
689
690 // write
691 fd = creat(filename, 0666);
692 if (fd == -1) {
693 fprintf(stderr, "error opening %s\n", filename);
694 return 1;
695 }
696
697 String8 filenames[4];
698 FileState states[4];
699 FileRec r;
700 r.deleted = false;
701
702 states[0].modTime_sec = 0xfedcba98;
703 states[0].modTime_nsec = 0xdeadbeef;
704 states[0].mode = 0777; // decimal 511, hex 0x000001ff
705 states[0].size = 0xababbcbc;
706 states[0].crc32 = 0x12345678;
707 states[0].nameLen = -12;
708 r.s = states[0];
709 filenames[0] = String8("bytes_of_padding");
710 snapshot.add(filenames[0], r);
711
712 states[1].modTime_sec = 0x93400031;
713 states[1].modTime_nsec = 0xdeadbeef;
714 states[1].mode = 0666; // decimal 438, hex 0x000001b6
715 states[1].size = 0x88557766;
716 states[1].crc32 = 0x22334422;
717 states[1].nameLen = -1;
718 r.s = states[1];
719 filenames[1] = String8("bytes_of_padding3");
720 snapshot.add(filenames[1], r);
721
722 states[2].modTime_sec = 0x33221144;
723 states[2].modTime_nsec = 0xdeadbeef;
724 states[2].mode = 0744; // decimal 484, hex 0x000001e4
725 states[2].size = 0x11223344;
726 states[2].crc32 = 0x01122334;
727 states[2].nameLen = 0;
728 r.s = states[2];
729 filenames[2] = String8("bytes_of_padding_2");
730 snapshot.add(filenames[2], r);
731
732 states[3].modTime_sec = 0x33221144;
733 states[3].modTime_nsec = 0xdeadbeef;
734 states[3].mode = 0755; // decimal 493, hex 0x000001ed
735 states[3].size = 0x11223344;
736 states[3].crc32 = 0x01122334;
737 states[3].nameLen = 0;
738 r.s = states[3];
739 filenames[3] = String8("bytes_of_padding__1");
740 snapshot.add(filenames[3], r);
741
742 err = write_snapshot_file(fd, snapshot);
743
744 close(fd);
745
746 if (err != 0) {
747 fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
748 return err;
749 }
750
751 static const unsigned char correct_data[] = {
752 // header
753 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00,
754 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00,
755
756 // bytes_of_padding
757 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde,
758 0xff, 0x01, 0x00, 0x00, 0xbc, 0xbc, 0xab, 0xab,
759 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x00, 0x00,
760 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66,
761 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
762
763 // bytes_of_padding3
764 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde,
765 0xb6, 0x01, 0x00, 0x00, 0x66, 0x77, 0x55, 0x88,
766 0x22, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00,
767 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66,
768 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
769 0x33, 0xab, 0xab, 0xab,
770
771 // bytes of padding2
772 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde,
773 0xe4, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11,
774 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00,
775 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66,
776 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
777 0x5f, 0x32, 0xab, 0xab,
778
779 // bytes of padding3
780 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde,
781 0xed, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11,
782 0x34, 0x23, 0x12, 0x01, 0x13, 0x00, 0x00, 0x00,
783 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66,
784 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
785 0x5f, 0x5f, 0x31, 0xab
786 };
787
788 err = compare_file(filename, correct_data, sizeof(correct_data));
789 if (err != 0) {
790 return err;
791 }
792
793 // read
794 fd = open(filename, O_RDONLY);
795 if (fd == -1) {
796 fprintf(stderr, "error opening for read %s\n", filename);
797 return 1;
798 }
799
800
801 KeyedVector<String8,FileState> readSnapshot;
802 err = read_snapshot_file(fd, &readSnapshot);
803 if (err != 0) {
804 fprintf(stderr, "read_snapshot_file failed %d\n", err);
805 return err;
806 }
807
808 if (readSnapshot.size() != 4) {
809 fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
810 return 1;
811 }
812
813 bool matched = true;
814 for (size_t i=0; i<readSnapshot.size(); i++) {
815 const String8& name = readSnapshot.keyAt(i);
816 const FileState state = readSnapshot.valueAt(i);
817
818 if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
819 || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
820 || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
821 fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n"
822 " actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i,
823 states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
824 states[i].crc32, name.length(), filenames[i].string(),
825 state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
826 state.nameLen, name.string());
827 matched = false;
828 }
829 }
830
831 return matched ? 0 : 1;
832}
833
834// hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data
835const unsigned char DATA_GOLDEN_FILE[] = {
836 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
837 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
838 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
839 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
840 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
841 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
842 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
843 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
844 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
845 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
846 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
847 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
848 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
849 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
850 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
851 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
852 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
853 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
854 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
855 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00
856
857};
858const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
859
860static int
861test_write_header_and_entity(BackupDataWriter& writer, const char* str)
862{
863 int err;
864 String8 text(str);
865
866 err = writer.WriteEntityHeader(text, text.length()+1);
867 if (err != 0) {
868 fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
869 return err;
870 }
871
872 err = writer.WriteEntityData(text.string(), text.length()+1);
873 if (err != 0) {
874 fprintf(stderr, "write failed for data '%s'\n", text.string());
875 return errno;
876 }
877
878 return err;
879}
880
881int
882backup_helper_test_data_writer()
883{
884 int err;
885 int fd;
886 const char* filename = SCRATCH_DIR "data_writer.data";
887
888 system("rm -r " SCRATCH_DIR);
889 mkdir(SCRATCH_DIR, 0777);
890 mkdir(SCRATCH_DIR "data", 0777);
891
892 fd = creat(filename, 0666);
893 if (fd == -1) {
894 fprintf(stderr, "error creating: %s\n", strerror(errno));
895 return errno;
896 }
897
898 BackupDataWriter writer(fd);
899
900 err = 0;
901 err |= test_write_header_and_entity(writer, "no_padding_");
902 err |= test_write_header_and_entity(writer, "padded_to__3");
903 err |= test_write_header_and_entity(writer, "padded_to_2__");
904 err |= test_write_header_and_entity(writer, "padded_to1");
905
906 close(fd);
907
908 err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
909 if (err != 0) {
910 return err;
911 }
912
913 return err;
914}
915
916int
917test_read_header_and_entity(BackupDataReader& reader, const char* str)
918{
919 int err;
920 int bufSize = strlen(str)+1;
921 char* buf = (char*)malloc(bufSize);
922 String8 string;
923 int cookie = 0x11111111;
924 size_t actualSize;
925 bool done;
926 int type;
927 ssize_t nRead;
928
929 // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
930
931 err = reader.ReadNextHeader(&done, &type);
932 if (done) {
933 fprintf(stderr, "should not be done yet\n");
934 goto finished;
935 }
936 if (err != 0) {
937 fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
938 goto finished;
939 }
940 if (type != BACKUP_HEADER_ENTITY_V1) {
941 err = EINVAL;
942 fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
943 }
944
945 err = reader.ReadEntityHeader(&string, &actualSize);
946 if (err != 0) {
947 fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
948 goto finished;
949 }
950 if (string != str) {
951 fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
952 err = EINVAL;
953 goto finished;
954 }
955 if ((int)actualSize != bufSize) {
956 fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
957 actualSize);
958 err = EINVAL;
959 goto finished;
960 }
961
962 nRead = reader.ReadEntityData(buf, bufSize);
963 if (nRead < 0) {
964 err = reader.Status();
965 fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
966 goto finished;
967 }
968
969 if (0 != memcmp(buf, str, bufSize)) {
970 fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
971 "%02x %02x %02x %02x '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
972 buf[0], buf[1], buf[2], buf[3]);
973 err = EINVAL;
974 goto finished;
975 }
976
977 // The next read will confirm whether it got the right amount of data.
978
979finished:
980 if (err != NO_ERROR) {
981 fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
982 }
983 free(buf);
984 return err;
985}
986
987int
988backup_helper_test_data_reader()
989{
990 int err;
991 int fd;
992 const char* filename = SCRATCH_DIR "data_reader.data";
993
994 system("rm -r " SCRATCH_DIR);
995 mkdir(SCRATCH_DIR, 0777);
996 mkdir(SCRATCH_DIR "data", 0777);
997
998 fd = creat(filename, 0666);
999 if (fd == -1) {
1000 fprintf(stderr, "error creating: %s\n", strerror(errno));
1001 return errno;
1002 }
1003
1004 err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1005 if (err != DATA_GOLDEN_FILE_SIZE) {
1006 fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
1007 return errno;
1008 }
1009
1010 close(fd);
1011
1012 fd = open(filename, O_RDONLY);
1013 if (fd == -1) {
1014 fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
1015 filename);
1016 return errno;
1017 }
1018
1019 {
1020 BackupDataReader reader(fd);
1021
1022 err = 0;
1023
1024 if (err == NO_ERROR) {
1025 err = test_read_header_and_entity(reader, "no_padding_");
1026 }
1027
1028 if (err == NO_ERROR) {
1029 err = test_read_header_and_entity(reader, "padded_to__3");
1030 }
1031
1032 if (err == NO_ERROR) {
1033 err = test_read_header_and_entity(reader, "padded_to_2__");
1034 }
1035
1036 if (err == NO_ERROR) {
1037 err = test_read_header_and_entity(reader, "padded_to1");
1038 }
1039 }
1040
1041 close(fd);
1042
1043 return err;
1044}
1045
1046static int
1047get_mod_time(const char* filename, struct timeval times[2])
1048{
1049 int err;
1050 struct stat64 st;
1051 err = stat64(filename, &st);
1052 if (err != 0) {
1053 fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
1054 return errno;
1055 }
1056 times[0].tv_sec = st.st_atime;
1057 times[1].tv_sec = st.st_mtime;
1058
1059 // If st_atime is a macro then struct stat64 uses struct timespec
1060 // to store the access and modif time values and typically
1061 // st_*time_nsec is not defined. In glibc, this is controlled by
1062 // __USE_MISC.
1063#ifdef __USE_MISC
1064#if !defined(st_atime) || defined(st_atime_nsec)
1065#error "Check if this __USE_MISC conditional is still needed."
1066#endif
1067 times[0].tv_usec = st.st_atim.tv_nsec / 1000;
1068 times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1069#else
1070 times[0].tv_usec = st.st_atime_nsec / 1000;
1071 times[1].tv_usec = st.st_mtime_nsec / 1000;
1072#endif
1073
1074 return 0;
1075}
1076
1077int
1078backup_helper_test_files()
1079{
1080 int err;
1081 int oldSnapshotFD;
1082 int dataStreamFD;
1083 int newSnapshotFD;
1084
1085 system("rm -r " SCRATCH_DIR);
1086 mkdir(SCRATCH_DIR, 0777);
1087 mkdir(SCRATCH_DIR "data", 0777);
1088
1089 write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1090 write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1091 write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
1092 write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
1093 write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
1094 write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
1095
1096 char const* files_before[] = {
1097 SCRATCH_DIR "data/b",
1098 SCRATCH_DIR "data/c",
1099 SCRATCH_DIR "data/d",
1100 SCRATCH_DIR "data/e",
1101 SCRATCH_DIR "data/f"
1102 };
1103
1104 char const* keys_before[] = {
1105 "data/b",
1106 "data/c",
1107 "data/d",
1108 "data/e",
1109 "data/f"
1110 };
1111
1112 dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
1113 if (dataStreamFD == -1) {
1114 fprintf(stderr, "error creating: %s\n", strerror(errno));
1115 return errno;
1116 }
1117
1118 newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
1119 if (newSnapshotFD == -1) {
1120 fprintf(stderr, "error creating: %s\n", strerror(errno));
1121 return errno;
1122 }
1123
1124 {
1125 BackupDataWriter dataStream(dataStreamFD);
1126
1127 err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
1128 if (err != 0) {
1129 return err;
1130 }
1131 }
1132
1133 close(dataStreamFD);
1134 close(newSnapshotFD);
1135
1136 sleep(3);
1137
1138 struct timeval d_times[2];
1139 struct timeval e_times[2];
1140
1141 err = get_mod_time(SCRATCH_DIR "data/d", d_times);
1142 err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
1143 if (err != 0) {
1144 return err;
1145 }
1146
1147 write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1148 unlink(SCRATCH_DIR "data/c");
1149 write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1150 write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
1151 utimes(SCRATCH_DIR "data/d", d_times);
1152 write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
1153 utimes(SCRATCH_DIR "data/e", e_times);
1154 write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
1155 unlink(SCRATCH_DIR "data/f");
1156
1157 char const* files_after[] = {
1158 SCRATCH_DIR "data/a", // added
1159 SCRATCH_DIR "data/b", // same
1160 SCRATCH_DIR "data/c", // different mod time
1161 SCRATCH_DIR "data/d", // different size (same mod time)
1162 SCRATCH_DIR "data/e", // different contents (same mod time, same size)
1163 SCRATCH_DIR "data/g" // added
1164 };
1165
1166 char const* keys_after[] = {
1167 "data/a", // added
1168 "data/b", // same
1169 "data/c", // different mod time
1170 "data/d", // different size (same mod time)
1171 "data/e", // different contents (same mod time, same size)
1172 "data/g" // added
1173 };
1174
1175 oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
1176 if (oldSnapshotFD == -1) {
1177 fprintf(stderr, "error opening: %s\n", strerror(errno));
1178 return errno;
1179 }
1180
1181 dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
1182 if (dataStreamFD == -1) {
1183 fprintf(stderr, "error creating: %s\n", strerror(errno));
1184 return errno;
1185 }
1186
1187 newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
1188 if (newSnapshotFD == -1) {
1189 fprintf(stderr, "error creating: %s\n", strerror(errno));
1190 return errno;
1191 }
1192
1193 {
1194 BackupDataWriter dataStream(dataStreamFD);
1195
1196 err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
1197 if (err != 0) {
1198 return err;
1199 }
1200}
1201
1202 close(oldSnapshotFD);
1203 close(dataStreamFD);
1204 close(newSnapshotFD);
1205
1206 return 0;
1207}
1208
1209int
1210backup_helper_test_null_base()
1211{
1212 int err;
1213 int oldSnapshotFD;
1214 int dataStreamFD;
1215 int newSnapshotFD;
1216
1217 system("rm -r " SCRATCH_DIR);
1218 mkdir(SCRATCH_DIR, 0777);
1219 mkdir(SCRATCH_DIR "data", 0777);
1220
1221 write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1222
1223 char const* files[] = {
1224 SCRATCH_DIR "data/a",
1225 };
1226
1227 char const* keys[] = {
1228 "a",
1229 };
1230
1231 dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1232 if (dataStreamFD == -1) {
1233 fprintf(stderr, "error creating: %s\n", strerror(errno));
1234 return errno;
1235 }
1236
1237 newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1238 if (newSnapshotFD == -1) {
1239 fprintf(stderr, "error creating: %s\n", strerror(errno));
1240 return errno;
1241 }
1242
1243 {
1244 BackupDataWriter dataStream(dataStreamFD);
1245
1246 err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1247 if (err != 0) {
1248 return err;
1249 }
1250 }
1251
1252 close(dataStreamFD);
1253 close(newSnapshotFD);
1254
1255 return 0;
1256}
1257
1258int
1259backup_helper_test_missing_file()
1260{
1261 int err;
1262 int oldSnapshotFD;
1263 int dataStreamFD;
1264 int newSnapshotFD;
1265
1266 system("rm -r " SCRATCH_DIR);
1267 mkdir(SCRATCH_DIR, 0777);
1268 mkdir(SCRATCH_DIR "data", 0777);
1269
1270 write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1271
1272 char const* files[] = {
1273 SCRATCH_DIR "data/a",
1274 SCRATCH_DIR "data/b",
1275 SCRATCH_DIR "data/c",
1276 };
1277
1278 char const* keys[] = {
1279 "a",
1280 "b",
1281 "c",
1282 };
1283
1284 dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1285 if (dataStreamFD == -1) {
1286 fprintf(stderr, "error creating: %s\n", strerror(errno));
1287 return errno;
1288 }
1289
1290 newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1291 if (newSnapshotFD == -1) {
1292 fprintf(stderr, "error creating: %s\n", strerror(errno));
1293 return errno;
1294 }
1295
1296 {
1297 BackupDataWriter dataStream(dataStreamFD);
1298
1299 err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1300 if (err != 0) {
1301 return err;
1302 }
1303 }
1304
1305 close(dataStreamFD);
1306 close(newSnapshotFD);
1307
1308 return 0;
1309}
1310
1311
1312#endif // TEST_BACKUP_HELPERS
1313
1314}