BackupHelpers.cpp revision 2e1da32203b7f6df76023f25a7382a31fad6b19d
1/*
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/backup_helpers.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 <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <utime.h>
33#include <fcntl.h>
34#include <zlib.h>
35
36#include <cutils/log.h>
37
38namespace android {
39
40#define MAGIC0 0x70616e53 // Snap
41#define MAGIC1 0x656c6946 // File
42
43#if TEST_BACKUP_HELPERS
44#define LOGP(x...) printf(x)
45#else
46#define LOGP(x...) LOGD(x)
47#endif
48
49struct SnapshotHeader {
50    int magic0;
51    int fileCount;
52    int magic1;
53    int totalSize;
54};
55
56struct FileState {
57    int modTime_sec;
58    int modTime_nsec;
59    int size;
60    int crc32;
61    int nameLen;
62};
63
64const static int ROUND_UP[4] = { 0, 3, 2, 1 };
65
66static inline int
67round_up(int n)
68{
69    return n + ROUND_UP[n % 4];
70}
71
72static int
73read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
74{
75    int bytesRead = 0;
76    int amt;
77    SnapshotHeader header;
78
79    amt = read(fd, &header, sizeof(header));
80    if (amt != sizeof(header)) {
81        return errno;
82    }
83    bytesRead += amt;
84
85    if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
86        LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
87        return 1;
88    }
89
90    for (int i=0; i<header.fileCount; i++) {
91        FileState file;
92        char filenameBuf[128];
93
94        amt = read(fd, &file, sizeof(file));
95        if (amt != sizeof(file)) {
96            LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
97            return 1;
98        }
99        bytesRead += amt;
100
101        // filename is not NULL terminated, but it is padded
102        int nameBufSize = round_up(file.nameLen);
103        char* filename = nameBufSize <= (int)sizeof(filenameBuf)
104                ? filenameBuf
105                : (char*)malloc(nameBufSize);
106        amt = read(fd, filename, nameBufSize);
107        if (amt == nameBufSize) {
108            snapshot->add(String8(filename, file.nameLen), file);
109        }
110        bytesRead += amt;
111        if (filename != filenameBuf) {
112            free(filename);
113        }
114        if (amt != nameBufSize) {
115            LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
116            return 1;
117        }
118    }
119
120    if (header.totalSize != bytesRead) {
121        LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
122                header.totalSize, bytesRead);
123        return 1;
124    }
125
126    return 0;
127}
128
129static int
130write_snapshot_file(int fd, const KeyedVector<String8,FileState>& snapshot)
131{
132    int bytesWritten = sizeof(SnapshotHeader);
133    // preflight size
134    const int N = snapshot.size();
135    for (int i=0; i<N; i++) {
136        const String8& name = snapshot.keyAt(i);
137        bytesWritten += sizeof(FileState) + round_up(name.length());
138    }
139
140    LOGP("write_snapshot_file fd=%d\n", fd);
141
142    int amt;
143    SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten };
144
145    amt = write(fd, &header, sizeof(header));
146    if (amt != sizeof(header)) {
147        LOGW("write_snapshot_file error writing header %s", strerror(errno));
148        return errno;
149    }
150
151    for (int i=0; i<header.fileCount; i++) {
152        const String8& name = snapshot.keyAt(i);
153        FileState file = snapshot.valueAt(i);
154        int nameLen = file.nameLen = name.length();
155
156        amt = write(fd, &file, sizeof(file));
157        if (amt != sizeof(file)) {
158            LOGW("write_snapshot_file error writing header %s", strerror(errno));
159            return 1;
160        }
161
162        // filename is not NULL terminated, but it is padded
163        amt = write(fd, name.string(), nameLen);
164        if (amt != nameLen) {
165            LOGW("write_snapshot_file error writing filename %s", strerror(errno));
166            return 1;
167        }
168        int paddingLen = ROUND_UP[nameLen % 4];
169        if (paddingLen != 0) {
170            int padding = 0xabababab;
171            amt = write(fd, &padding, paddingLen);
172            if (amt != paddingLen) {
173                LOGW("write_snapshot_file error writing %d bytes of filename padding %s",
174                        paddingLen, strerror(errno));
175                return 1;
176            }
177        }
178    }
179
180    return 0;
181}
182
183static int
184write_delete_file(const String8& key)
185{
186    LOGP("write_delete_file %s\n", key.string());
187    return 0;
188}
189
190static int
191write_update_file(const String8& realFilename, const String8& key)
192{
193    LOGP("write_update_file %s (%s)\n", realFilename.string(), key.string());
194    return 0;
195}
196
197static int
198compute_crc32(const String8& filename)
199{
200    const int bufsize = 4*1024;
201    int amt;
202
203    int fd = open(filename.string(), O_RDONLY);
204    if (fd == -1) {
205        return -1;
206    }
207
208    char* buf = (char*)malloc(bufsize);
209    int crc = crc32(0L, Z_NULL, 0);
210
211    while ((amt = read(fd, buf, bufsize)) != 0) {
212        crc = crc32(crc, (Bytef*)buf, amt);
213    }
214
215    close(fd);
216    free(buf);
217
218    return crc;
219}
220
221int
222back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD,
223        char const* fileBase, char const* const* files, int fileCount)
224{
225    int err;
226    const String8 base(fileBase);
227    KeyedVector<String8,FileState> oldSnapshot;
228    KeyedVector<String8,FileState> newSnapshot;
229
230    if (oldSnapshotFD != -1) {
231        err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
232        if (err != 0) {
233            // On an error, treat this as a full backup.
234            oldSnapshot.clear();
235        }
236    }
237
238    for (int i=0; i<fileCount; i++) {
239        String8 name(files[i]);
240        FileState s;
241        struct stat st;
242        String8 realFilename(base);
243        realFilename.appendPath(name);
244
245        err = stat(realFilename.string(), &st);
246        if (err != 0) {
247            LOGW("Error stating file %s", realFilename.string());
248            continue;
249        }
250
251        s.modTime_sec = st.st_mtime;
252        s.modTime_nsec = 0; // workaround sim breakage
253        //s.modTime_nsec = st.st_mtime_nsec;
254        s.size = st.st_size;
255        s.crc32 = compute_crc32(realFilename);
256
257        newSnapshot.add(name, s);
258    }
259
260    int n = 0;
261    int N = oldSnapshot.size();
262    int m = 0;
263
264    while (n<N && m<fileCount) {
265        const String8& p = oldSnapshot.keyAt(n);
266        const String8& q = newSnapshot.keyAt(m);
267        int cmp = p.compare(q);
268        if (cmp > 0) {
269            // file added
270            String8 realFilename(base);
271            realFilename.appendPath(q);
272            LOGP("file added: %s\n", realFilename.string());
273            write_update_file(realFilename, q);
274            m++;
275        }
276        else if (cmp < 0) {
277            // file removed
278            LOGP("file removed: %s\n", p.string());
279            write_delete_file(p);
280            n++;
281        }
282        else {
283            // both files exist, check them
284            String8 realFilename(base);
285            realFilename.appendPath(q);
286            const FileState& f = oldSnapshot.valueAt(n);
287            const FileState& g = newSnapshot.valueAt(m);
288
289            LOGP("%s\n", q.string());
290            LOGP("  new: modTime=%d,%d size=%-3d crc32=0x%08x\n",
291                    f.modTime_sec, f.modTime_nsec, f.size, f.crc32);
292            LOGP("  old: modTime=%d,%d size=%-3d crc32=0x%08x\n",
293                    g.modTime_sec, g.modTime_nsec, g.size, g.crc32);
294            if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec
295                    || f.size != g.size || f.crc32 != g.crc32) {
296                write_update_file(realFilename, p);
297            }
298            n++;
299            m++;
300        }
301    }
302
303    // these were deleted
304    while (n<N) {
305        write_delete_file(oldSnapshot.keyAt(n));
306        n++;
307    }
308
309    // these were added
310    while (m<fileCount) {
311        const String8& q = newSnapshot.keyAt(m);
312        String8 realFilename(base);
313        realFilename.appendPath(q);
314        write_update_file(realFilename, q);
315        m++;
316    }
317
318    err = write_snapshot_file(newSnapshotFD, newSnapshot);
319
320    return 0;
321}
322
323#if TEST_BACKUP_HELPERS
324
325#define SCRATCH_DIR "/data/backup_helper_test/"
326
327static int
328write_text_file(const char* path, const char* data)
329{
330    int amt;
331    int fd;
332    int len;
333
334    fd = creat(path, 0666);
335    if (fd == -1) {
336        fprintf(stderr, "creat %s failed\n", path);
337        return errno;
338    }
339
340    len = strlen(data);
341    amt = write(fd, data, len);
342    if (amt != len) {
343        fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
344        return errno;
345    }
346
347    close(fd);
348
349    return 0;
350}
351
352static int
353compare_file(const char* path, const unsigned char* data, int len)
354{
355    int fd;
356    int amt;
357
358    fd = open(path, O_RDONLY);
359    if (fd == -1) {
360        fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
361        return errno;
362    }
363
364    unsigned char* contents = (unsigned char*)malloc(len);
365    if (contents == NULL) {
366        fprintf(stderr, "malloc(%d) failed\n", len);
367        return ENOMEM;
368    }
369
370    bool sizesMatch = true;
371    amt = lseek(fd, 0, SEEK_END);
372    if (amt != len) {
373        fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
374        sizesMatch = false;
375    }
376    lseek(fd, 0, SEEK_SET);
377
378    int readLen = amt < len ? amt : len;
379    amt = read(fd, contents, readLen);
380    if (amt != readLen) {
381        fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
382    }
383
384    bool contentsMatch = true;
385    for (int i=0; i<readLen; i++) {
386        if (data[i] != contents[i]) {
387            if (contentsMatch) {
388                fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
389                contentsMatch = false;
390            }
391            fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
392        }
393    }
394
395    return contentsMatch && sizesMatch ? 0 : 1;
396}
397
398int
399backup_helper_test_empty()
400{
401    int err;
402    int fd;
403    KeyedVector<String8,FileState> snapshot;
404    const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
405
406    system("rm -r " SCRATCH_DIR);
407    mkdir(SCRATCH_DIR, 0777);
408
409    // write
410    fd = creat(filename, 0666);
411    if (fd == -1) {
412        fprintf(stderr, "error creating %s\n", filename);
413        return 1;
414    }
415
416    err = write_snapshot_file(fd, snapshot);
417
418    close(fd);
419
420    if (err != 0) {
421        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
422        return err;
423    }
424
425    static const unsigned char correct_data[] = {
426        0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
427        0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
428    };
429
430    err = compare_file(filename, correct_data, sizeof(correct_data));
431    if (err != 0) {
432        return err;
433    }
434
435    // read
436    fd = open(filename, O_RDONLY);
437    if (fd == -1) {
438        fprintf(stderr, "error opening for read %s\n", filename);
439        return 1;
440    }
441
442    KeyedVector<String8,FileState> readSnapshot;
443    err = read_snapshot_file(fd, &readSnapshot);
444    if (err != 0) {
445        fprintf(stderr, "read_snapshot_file failed %d\n", err);
446        return err;
447    }
448
449    if (readSnapshot.size() != 0) {
450        fprintf(stderr, "readSnapshot should be length 0\n");
451        return 1;
452    }
453
454    return 0;
455}
456
457int
458backup_helper_test_four()
459{
460    int err;
461    int fd;
462    KeyedVector<String8,FileState> snapshot;
463    const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
464
465    system("rm -r " SCRATCH_DIR);
466    mkdir(SCRATCH_DIR, 0777);
467
468    // write
469    fd = creat(filename, 0666);
470    if (fd == -1) {
471        fprintf(stderr, "error opening %s\n", filename);
472        return 1;
473    }
474
475    String8 filenames[4];
476    FileState states[4];
477
478    states[0].modTime_sec = 0xfedcba98;
479    states[0].modTime_nsec = 0xdeadbeef;
480    states[0].size = 0xababbcbc;
481    states[0].crc32 = 0x12345678;
482    states[0].nameLen = -12;
483    filenames[0] = String8("bytes_of_padding");
484    snapshot.add(filenames[0], states[0]);
485
486    states[1].modTime_sec = 0x93400031;
487    states[1].modTime_nsec = 0xdeadbeef;
488    states[1].size = 0x88557766;
489    states[1].crc32 = 0x22334422;
490    states[1].nameLen = -1;
491    filenames[1] = String8("bytes_of_padding3");
492    snapshot.add(filenames[1], states[1]);
493
494    states[2].modTime_sec = 0x33221144;
495    states[2].modTime_nsec = 0xdeadbeef;
496    states[2].size = 0x11223344;
497    states[2].crc32 = 0x01122334;
498    states[2].nameLen = 0;
499    filenames[2] = String8("bytes_of_padding_2");
500    snapshot.add(filenames[2], states[2]);
501
502    states[3].modTime_sec = 0x33221144;
503    states[3].modTime_nsec = 0xdeadbeef;
504    states[3].size = 0x11223344;
505    states[3].crc32 = 0x01122334;
506    states[3].nameLen = 0;
507    filenames[3] = String8("bytes_of_padding__1");
508    snapshot.add(filenames[3], states[3]);
509
510    err = write_snapshot_file(fd, snapshot);
511
512    close(fd);
513
514    if (err != 0) {
515        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
516        return err;
517    }
518
519    static const unsigned char correct_data[] = {
520        // header
521        0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
522        0x46, 0x69, 0x6c, 0x65,  0xac, 0x00, 0x00, 0x00,
523
524        // bytes_of_padding
525        0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
526        0xbc, 0xbc, 0xab, 0xab,  0x78, 0x56, 0x34, 0x12,
527        0x10, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
528        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
529        0x64, 0x69, 0x6e, 0x67,
530
531        // bytes_of_padding3
532        0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
533        0x66, 0x77, 0x55, 0x88,  0x22, 0x44, 0x33, 0x22,
534        0x11, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
535        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
536        0x64, 0x69, 0x6e, 0x67,  0x33, 0xab, 0xab, 0xab,
537
538        // bytes of padding2
539        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
540        0x44, 0x33, 0x22, 0x11,  0x34, 0x23, 0x12, 0x01,
541        0x12, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
542        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
543        0x64, 0x69, 0x6e, 0x67,  0x5f, 0x32, 0xab, 0xab,
544
545        // bytes of padding3
546        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
547        0x44, 0x33, 0x22, 0x11,  0x34, 0x23, 0x12, 0x01,
548        0x13, 0x00, 0x00, 0x00,  0x62, 0x79, 0x74, 0x65,
549        0x73, 0x5f, 0x6f, 0x66,  0x5f, 0x70, 0x61, 0x64,
550        0x64, 0x69, 0x6e, 0x67,  0x5f, 0x5f, 0x31, 0xab
551    };
552
553    err = compare_file(filename, correct_data, sizeof(correct_data));
554    if (err != 0) {
555        return err;
556    }
557
558    // read
559    fd = open(filename, O_RDONLY);
560    if (fd == -1) {
561        fprintf(stderr, "error opening for read %s\n", filename);
562        return 1;
563    }
564
565
566    KeyedVector<String8,FileState> readSnapshot;
567    err = read_snapshot_file(fd, &readSnapshot);
568    if (err != 0) {
569        fprintf(stderr, "read_snapshot_file failed %d\n", err);
570        return err;
571    }
572
573    if (readSnapshot.size() != 4) {
574        fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
575        return 1;
576    }
577
578    bool matched = true;
579    for (size_t i=0; i<readSnapshot.size(); i++) {
580        const String8& name = readSnapshot.keyAt(i);
581        const FileState state = readSnapshot.valueAt(i);
582
583        if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
584                || states[i].modTime_nsec != state.modTime_nsec
585                || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
586            fprintf(stderr, "state %d expected={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n"
587                            "          actual={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n", i,
588                    states[i].modTime_sec, states[i].modTime_nsec, states[i].size, states[i].crc32,
589                    name.length(), filenames[i].string(),
590                    state.modTime_sec, state.modTime_nsec, state.size, state.crc32, state.nameLen,
591                    name.string());
592            matched = false;
593        }
594    }
595
596    return matched ? 0 : 1;
597}
598
599// hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
600const unsigned char DATA_GOLDEN_FILE[] = {
601     0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00,
602     0xdd, 0xcc, 0xbb, 0xaa, 0x6e, 0x6f, 0x5f, 0x70,
603     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
604     0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
605     0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
606     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
607     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
608     0x6e, 0x67, 0x5f, 0x00, 0x41, 0x70, 0x70, 0x31,
609     0x0c, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
610     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
611     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
612     0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00,
613     0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
614     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
615     0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
616     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
617     0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31,
618     0x0d, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
619     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
620     0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc,
621     0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
622     0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
623     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
624     0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
625     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
626     0x5f, 0x00, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31,
627     0x0a, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
628     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
629     0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61,
630     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
631     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
632     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
633     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc,
634     0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00,
635     0x99, 0x99, 0x77, 0x77
636};
637const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
638
639static int
640test_write_header_and_entity(BackupDataWriter& writer, const char* str)
641{
642    int err;
643    String8 text(str);
644
645    err = writer.WriteAppHeader(text, 0xaabbccdd);
646    if (err != 0) {
647        fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err));
648        return err;
649    }
650
651    err = writer.WriteEntityHeader(text, text.length()+1);
652    if (err != 0) {
653        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
654        return err;
655    }
656
657    err = writer.WriteEntityData(text.string(), text.length()+1);
658    if (err != 0) {
659        fprintf(stderr, "write failed for data '%s'\n", text.string());
660        return errno;
661    }
662
663    return err;
664}
665
666int
667backup_helper_test_data_writer()
668{
669    int err;
670    int fd;
671    const char* filename = SCRATCH_DIR "data_writer.data";
672
673    system("rm -r " SCRATCH_DIR);
674    mkdir(SCRATCH_DIR, 0777);
675    mkdir(SCRATCH_DIR "data", 0777);
676
677    fd = creat(filename, 0666);
678    if (fd == -1) {
679        fprintf(stderr, "error creating: %s\n", strerror(errno));
680        return errno;
681    }
682
683    BackupDataWriter writer(fd);
684
685    err = 0;
686    err |= test_write_header_and_entity(writer, "no_padding_");
687    err |= test_write_header_and_entity(writer, "padded_to__3");
688    err |= test_write_header_and_entity(writer, "padded_to_2__");
689    err |= test_write_header_and_entity(writer, "padded_to1");
690
691    writer.WriteAppFooter(0x77779999);
692
693    close(fd);
694
695    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
696    if (err != 0) {
697        return err;
698    }
699
700    return err;
701}
702
703int
704test_read_header_and_entity(BackupDataReader& reader, const char* str)
705{
706    int err;
707    int bufSize = strlen(str)+1;
708    char* buf = (char*)malloc(bufSize);
709    String8 string;
710    int cookie = 0x11111111;
711    size_t actualSize;
712
713    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
714
715    err = reader.ReadNextHeader();
716    if (err != 0) {
717        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
718        goto done;
719    }
720
721    err = reader.ReadAppHeader(&string, &cookie);
722    if (err != 0) {
723        fprintf(stderr, "ReadAppHeader failed with %s\n", strerror(err));
724        goto done;
725    }
726    if (string != str) {
727        fprintf(stderr, "ReadAppHeader expected packageName '%s' got '%s'\n", str, string.string());
728        err = EINVAL;
729        goto done;
730    }
731    if (cookie != (int)0xaabbccdd) {
732        fprintf(stderr, "ReadAppHeader expected cookie 0x%08x got 0x%08x\n", 0xaabbccdd, cookie);
733        err = EINVAL;
734        goto done;
735    }
736
737    err = reader.ReadNextHeader();
738    if (err != 0) {
739        fprintf(stderr, "ReadNextHeader (for entity header) failed with %s\n", strerror(err));
740        goto done;
741    }
742
743    err = reader.ReadEntityHeader(&string, &actualSize);
744    if (err != 0) {
745        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
746        goto done;
747    }
748    if (string != str) {
749        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
750        err = EINVAL;
751        goto done;
752    }
753    if ((int)actualSize != bufSize) {
754        fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
755                actualSize);
756        err = EINVAL;
757        goto done;
758    }
759
760    err = reader.ReadEntityData(buf, bufSize);
761    if (err != NO_ERROR) {
762        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
763        goto done;
764    }
765
766    if (0 != memcmp(buf, str, bufSize)) {
767        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
768                "%02x %02x %02x %02x\n", str, buf[0], buf[1], buf[2], buf[3]);
769        err = EINVAL;
770        goto done;
771    }
772
773    // The next read will confirm whether it got the right amount of data.
774
775done:
776    if (err != NO_ERROR) {
777        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
778    }
779    free(buf);
780    return err;
781}
782
783int
784backup_helper_test_data_reader()
785{
786    int err;
787    int fd;
788    const char* filename = SCRATCH_DIR "data_reader.data";
789
790    system("rm -r " SCRATCH_DIR);
791    mkdir(SCRATCH_DIR, 0777);
792    mkdir(SCRATCH_DIR "data", 0777);
793
794    fd = creat(filename, 0666);
795    if (fd == -1) {
796        fprintf(stderr, "error creating: %s\n", strerror(errno));
797        return errno;
798    }
799
800    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
801    if (err != DATA_GOLDEN_FILE_SIZE) {
802        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
803        return errno;
804    }
805
806    close(fd);
807
808    fd = open(filename, O_RDONLY);
809    if (fd == -1) {
810        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
811                filename);
812        return errno;
813    }
814
815    {
816        BackupDataReader reader(fd);
817
818        err = 0;
819
820        if (err == NO_ERROR) {
821            err = test_read_header_and_entity(reader, "no_padding_");
822        }
823
824        if (err == NO_ERROR) {
825            err = test_read_header_and_entity(reader, "padded_to__3");
826        }
827
828        if (err == NO_ERROR) {
829            err = test_read_header_and_entity(reader, "padded_to_2__");
830        }
831
832        if (err == NO_ERROR) {
833            err = test_read_header_and_entity(reader, "padded_to1");
834        }
835
836        if (err == NO_ERROR) {
837            err = reader.ReadNextHeader();
838            if (err != 0) {
839                fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
840            }
841
842            if (err == NO_ERROR) {
843                int cookie;
844                err |= reader.ReadAppFooter(&cookie);
845                if (cookie != 0x77779999) {
846                    fprintf(stderr, "app footer cookie expected=0x%08x actual=0x%08x\n",
847                        0x77779999, cookie);
848                    err = EINVAL;
849                }
850            }
851        }
852    }
853
854    close(fd);
855
856    return err;
857}
858
859static int
860get_mod_time(const char* filename, struct timeval times[2])
861{
862    int err;
863    struct stat64 st;
864    err = stat64(filename, &st);
865    if (err != 0) {
866        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
867        return errno;
868    }
869    times[0].tv_sec = st.st_atime;
870    times[0].tv_usec = st.st_atime_nsec / 1000;
871    times[1].tv_sec = st.st_mtime;
872    times[1].tv_usec = st.st_mtime_nsec / 1000;
873    return 0;
874}
875
876int
877backup_helper_test_files()
878{
879    int err;
880    int oldSnapshotFD;
881    int dataStreamFD;
882    int newSnapshotFD;
883
884    system("rm -r " SCRATCH_DIR);
885    mkdir(SCRATCH_DIR, 0777);
886    mkdir(SCRATCH_DIR "data", 0777);
887
888    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
889    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
890    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
891    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
892    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
893    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
894
895    char const* files_before[] = {
896        "data/b",
897        "data/c",
898        "data/d",
899        "data/e",
900        "data/f"
901    };
902
903    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
904    if (dataStreamFD == -1) {
905        fprintf(stderr, "error creating: %s\n", strerror(errno));
906        return errno;
907    }
908
909    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
910    if (newSnapshotFD == -1) {
911        fprintf(stderr, "error creating: %s\n", strerror(errno));
912        return errno;
913    }
914
915    err = back_up_files(-1, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_before, 5);
916    if (err != 0) {
917        return err;
918    }
919
920    close(dataStreamFD);
921    close(newSnapshotFD);
922
923    sleep(3);
924
925    struct timeval d_times[2];
926    struct timeval e_times[2];
927
928    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
929    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
930    if (err != 0) {
931        return err;
932    }
933
934    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
935    unlink(SCRATCH_DIR "data/c");
936    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
937    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
938    utimes(SCRATCH_DIR "data/d", d_times);
939    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
940    utimes(SCRATCH_DIR "data/e", e_times);
941    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
942    unlink(SCRATCH_DIR "data/f");
943
944    char const* files_after[] = {
945        "data/a", // added
946        "data/b", // same
947        "data/c", // different mod time
948        "data/d", // different size (same mod time)
949        "data/e", // different contents (same mod time, same size)
950        "data/g"  // added
951    };
952
953    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
954    if (oldSnapshotFD == -1) {
955        fprintf(stderr, "error opening: %s\n", strerror(errno));
956        return errno;
957    }
958
959    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
960    if (dataStreamFD == -1) {
961        fprintf(stderr, "error creating: %s\n", strerror(errno));
962        return errno;
963    }
964
965    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
966    if (newSnapshotFD == -1) {
967        fprintf(stderr, "error creating: %s\n", strerror(errno));
968        return errno;
969    }
970
971    err = back_up_files(oldSnapshotFD, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_after, 6);
972    if (err != 0) {
973        return err;
974    }
975
976    close(oldSnapshotFD);
977    close(dataStreamFD);
978    close(newSnapshotFD);
979
980    return 0;
981}
982
983#endif // TEST_BACKUP_HELPERS
984
985}
986