BackupHelpers.cpp revision 4535e40544aeb957d44fad75fbe5676effe03689
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     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
603     0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
604     0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
605     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
606     0x6e, 0x67, 0x5f, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
607     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
608     0x41, 0x70, 0x70, 0x31, 0x0c, 0x00, 0x00, 0x00,
609     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
610     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
611     0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00,
612     0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
613     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
614     0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
615     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
616     0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31,
617     0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
618     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
619     0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
620     0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
621     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
622     0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc,
623     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
624     0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc,
625     0x41, 0x70, 0x70, 0x31, 0x0a, 0x00, 0x00, 0x00,
626     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
627     0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61,
628     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
629     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
630     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
631     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc,
632     0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00,
633};
634const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
635
636static int
637test_write_header_and_entity(BackupDataWriter& writer, const char* str)
638{
639    int err;
640    String8 text(str);
641
642    err = writer.WriteAppHeader(text);
643    if (err != 0) {
644        fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err));
645        return err;
646    }
647
648    err = writer.WriteEntityHeader(text, text.length()+1);
649    if (err != 0) {
650        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
651        return err;
652    }
653
654    err = writer.WriteEntityData(text.string(), text.length()+1);
655    if (err != 0) {
656        fprintf(stderr, "write failed for data '%s'\n", text.string());
657        return errno;
658    }
659
660    return err;
661}
662
663int
664backup_helper_test_data_writer()
665{
666    int err;
667    int fd;
668    const char* filename = SCRATCH_DIR "data_writer.data";
669
670    system("rm -r " SCRATCH_DIR);
671    mkdir(SCRATCH_DIR, 0777);
672    mkdir(SCRATCH_DIR "data", 0777);
673
674    fd = creat(filename, 0666);
675    if (fd == -1) {
676        fprintf(stderr, "error creating: %s\n", strerror(errno));
677        return errno;
678    }
679
680    BackupDataWriter writer(fd);
681
682    err = 0;
683    err |= test_write_header_and_entity(writer, "no_padding_");
684    err |= test_write_header_and_entity(writer, "padded_to__3");
685    err |= test_write_header_and_entity(writer, "padded_to_2__");
686    err |= test_write_header_and_entity(writer, "padded_to1");
687
688    writer.WriteAppFooter();
689
690    close(fd);
691
692    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
693    if (err != 0) {
694        return err;
695    }
696
697    return err;
698}
699
700static int
701get_mod_time(const char* filename, struct timeval times[2])
702{
703    int err;
704    struct stat64 st;
705    err = stat64(filename, &st);
706    if (err != 0) {
707        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
708        return errno;
709    }
710    times[0].tv_sec = st.st_atime;
711    times[0].tv_usec = st.st_atime_nsec / 1000;
712    times[1].tv_sec = st.st_mtime;
713    times[1].tv_usec = st.st_mtime_nsec / 1000;
714    return 0;
715}
716
717int
718backup_helper_test_files()
719{
720    int err;
721    int oldSnapshotFD;
722    int dataStreamFD;
723    int newSnapshotFD;
724
725    system("rm -r " SCRATCH_DIR);
726    mkdir(SCRATCH_DIR, 0777);
727    mkdir(SCRATCH_DIR "data", 0777);
728
729    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
730    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
731    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
732    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
733    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
734    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
735
736    char const* files_before[] = {
737        "data/b",
738        "data/c",
739        "data/d",
740        "data/e",
741        "data/f"
742    };
743
744    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
745    if (dataStreamFD == -1) {
746        fprintf(stderr, "error creating: %s\n", strerror(errno));
747        return errno;
748    }
749
750    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
751    if (newSnapshotFD == -1) {
752        fprintf(stderr, "error creating: %s\n", strerror(errno));
753        return errno;
754    }
755
756    err = back_up_files(-1, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_before, 5);
757    if (err != 0) {
758        return err;
759    }
760
761    close(dataStreamFD);
762    close(newSnapshotFD);
763
764    sleep(3);
765
766    struct timeval d_times[2];
767    struct timeval e_times[2];
768
769    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
770    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
771    if (err != 0) {
772        return err;
773    }
774
775    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
776    unlink(SCRATCH_DIR "data/c");
777    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
778    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
779    utimes(SCRATCH_DIR "data/d", d_times);
780    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
781    utimes(SCRATCH_DIR "data/e", e_times);
782    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
783    unlink(SCRATCH_DIR "data/f");
784
785    char const* files_after[] = {
786        "data/a", // added
787        "data/b", // same
788        "data/c", // different mod time
789        "data/d", // different size (same mod time)
790        "data/e", // different contents (same mod time, same size)
791        "data/g"  // added
792    };
793
794    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
795    if (oldSnapshotFD == -1) {
796        fprintf(stderr, "error opening: %s\n", strerror(errno));
797        return errno;
798    }
799
800    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
801    if (dataStreamFD == -1) {
802        fprintf(stderr, "error creating: %s\n", strerror(errno));
803        return errno;
804    }
805
806    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
807    if (newSnapshotFD == -1) {
808        fprintf(stderr, "error creating: %s\n", strerror(errno));
809        return errno;
810    }
811
812    err = back_up_files(oldSnapshotFD, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_after, 6);
813    if (err != 0) {
814        return err;
815    }
816
817    close(oldSnapshotFD);
818    close(dataStreamFD);
819    close(newSnapshotFD);
820
821    return 0;
822}
823
824#endif // TEST_BACKUP_HELPERS
825
826}
827