BackupHelpers.cpp revision 16c4d154dca43c662571129af31b27433b919a32
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 <androidfw/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...) ALOGD(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        ALOGW("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            ALOGW("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            ALOGW("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        ALOGW("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        ALOGW("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                ALOGW("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                ALOGW("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                    ALOGW("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        ALOGE("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        ALOGE("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// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of
446// returning the initial dest, return a pointer to the trailing NUL.
447static char* strcpy_ptr(char* dest, const char* str) {
448    if (dest && str) {
449        while ((*dest = *str) != 0) {
450            dest++;
451            str++;
452        }
453    }
454    return dest;
455}
456
457static void calc_tar_checksum(char* buf) {
458    // [ 148 :   8 ] checksum -- to be calculated with this field as space chars
459    memset(buf + 148, ' ', 8);
460
461    uint16_t sum = 0;
462    for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) {
463        sum += *p;
464    }
465
466    // Now write the real checksum value:
467    // [ 148 :   8 ]  checksum: 6 octal digits [leading zeroes], NUL, SPC
468    sprintf(buf + 148, "%06o", sum); // the trailing space is already in place
469}
470
471// Returns number of bytes written
472static int write_pax_header_entry(char* buf, const char* key, const char* value) {
473    // start with the size of "1 key=value\n"
474    int len = strlen(key) + strlen(value) + 4;
475    if (len > 9) len++;
476    if (len > 99) len++;
477    if (len > 999) len++;
478    // since PATH_MAX is 4096 we don't expect to have to generate any single
479    // header entry longer than 9999 characters
480
481    return sprintf(buf, "%d %s=%s\n", len, key, value);
482}
483
484// Wire format to the backup manager service is chunked:  each chunk is prefixed by
485// a 4-byte count of its size.  A chunk size of zero (four zero bytes) indicates EOD.
486void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) {
487    uint32_t chunk_size_no = htonl(size);
488    writer->WriteEntityData(&chunk_size_no, 4);
489    if (size != 0) writer->WriteEntityData(buffer, size);
490}
491
492int write_tarfile(const String8& packageName, const String8& domain,
493        const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
494{
495    // In the output stream everything is stored relative to the root
496    const char* relstart = filepath.string() + rootpath.length();
497    if (*relstart == '/') relstart++;     // won't be true when path == rootpath
498    String8 relpath(relstart);
499
500    // If relpath is empty, it means this is the top of one of the standard named
501    // domain directories, so we should just skip it
502    if (relpath.length() == 0) {
503        return 0;
504    }
505
506    // Too long a name for the ustar format?
507    //    "apps/" + packagename + '/' + domainpath < 155 chars
508    //    relpath < 100 chars
509    bool needExtended = false;
510    if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) {
511        needExtended = true;
512    }
513
514    // Non-7bit-clean path also means needing pax extended format
515    if (!needExtended) {
516        for (size_t i = 0; i < filepath.length(); i++) {
517            if ((filepath[i] & 0x80) != 0) {
518                needExtended = true;
519                break;
520            }
521        }
522    }
523
524    int err = 0;
525    struct stat64 s;
526    if (lstat64(filepath.string(), &s) != 0) {
527        err = errno;
528        ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
529        return err;
530    }
531
532    String8 fullname;   // for pax later on
533    String8 prefix;
534
535    const int isdir = S_ISDIR(s.st_mode);
536    if (isdir) s.st_size = 0;   // directories get no actual data in the tar stream
537
538    // !!! TODO: use mmap when possible to avoid churning the buffer cache
539    // !!! TODO: this will break with symlinks; need to use readlink(2)
540    int fd = open(filepath.string(), O_RDONLY);
541    if (fd < 0) {
542        err = errno;
543        ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
544        return err;
545    }
546
547    // read/write up to this much at a time.
548    const size_t BUFSIZE = 32 * 1024;
549    char* buf = (char *)calloc(1,BUFSIZE);
550    char* paxHeader = buf + 512;    // use a different chunk of it as separate scratch
551    char* paxData = buf + 1024;
552
553    if (buf == NULL) {
554        ALOGE("Out of mem allocating transfer buffer");
555        err = ENOMEM;
556        goto done;
557    }
558
559    // Magic fields for the ustar file format
560    strcat(buf + 257, "ustar");
561    strcat(buf + 263, "00");
562
563    // [ 265 : 32 ] user name, ignored on restore
564    // [ 297 : 32 ] group name, ignored on restore
565
566    // [ 100 :   8 ] file mode
567    snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT);
568
569    // [ 108 :   8 ] uid -- ignored in Android format; uids are remapped at restore time
570    // [ 116 :   8 ] gid -- ignored in Android format
571    snprintf(buf + 108, 8, "0%lo", s.st_uid);
572    snprintf(buf + 116, 8, "0%lo", s.st_gid);
573
574    // [ 124 :  12 ] file size in bytes
575    if (s.st_size > 077777777777LL) {
576        // very large files need a pax extended size header
577        needExtended = true;
578    }
579    snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
580
581    // [ 136 :  12 ] last mod time as a UTC time_t
582    snprintf(buf + 136, 12, "%0lo", s.st_mtime);
583
584    // [ 156 :   1 ] link/file type
585    uint8_t type;
586    if (isdir) {
587        type = '5';     // tar magic: '5' == directory
588    } else if (S_ISREG(s.st_mode)) {
589        type = '0';     // tar magic: '0' == normal file
590    } else {
591        ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
592        goto cleanup;
593    }
594    buf[156] = type;
595
596    // [ 157 : 100 ] name of linked file [not implemented]
597
598    {
599        // Prefix and main relative path.  Path lengths have been preflighted.
600        if (packageName.length() > 0) {
601            prefix = "apps/";
602            prefix += packageName;
603        }
604        if (domain.length() > 0) {
605            prefix.appendPath(domain);
606        }
607
608        // pax extended means we don't put in a prefix field, and put a different
609        // string in the basic name field.  We can also construct the full path name
610        // out of the substrings we've now built.
611        fullname = prefix;
612        fullname.appendPath(relpath);
613
614        // ustar:
615        //    [   0 : 100 ]; file name/path
616        //    [ 345 : 155 ] filename path prefix
617        // We only use the prefix area if fullname won't fit in the path
618        if (fullname.length() > 100) {
619            strncpy(buf, relpath.string(), 100);
620            strncpy(buf + 345, prefix.string(), 155);
621        } else {
622            strncpy(buf, fullname.string(), 100);
623        }
624    }
625
626    // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used
627
628    ALOGI("   Name: %s", fullname.string());
629
630    // If we're using a pax extended header, build & write that here; lengths are
631    // already preflighted
632    if (needExtended) {
633        char sizeStr[32];   // big enough for a 64-bit unsigned value in decimal
634        char* p = paxData;
635
636        // construct the pax extended header data block
637        memset(paxData, 0, BUFSIZE - (paxData - buf));
638        int len;
639
640        // size header -- calc len in digits by actually rendering the number
641        // to a string - brute force but simple
642        snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size);
643        p += write_pax_header_entry(p, "size", sizeStr);
644
645        // fullname was generated above with the ustar paths
646        p += write_pax_header_entry(p, "path", fullname.string());
647
648        // Now we know how big the pax data is
649        int paxLen = p - paxData;
650
651        // Now build the pax *header* templated on the ustar header
652        memcpy(paxHeader, buf, 512);
653
654        String8 leaf = fullname.getPathLeaf();
655        memset(paxHeader, 0, 100);                  // rewrite the name area
656        snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string());
657        memset(paxHeader + 345, 0, 155);            // rewrite the prefix area
658        strncpy(paxHeader + 345, prefix.string(), 155);
659
660        paxHeader[156] = 'x';                       // mark it as a pax extended header
661
662        // [ 124 :  12 ] size of pax extended header data
663        memset(paxHeader + 124, 0, 12);
664        snprintf(paxHeader + 124, 12, "%011o", p - paxData);
665
666        // Checksum and write the pax block header
667        calc_tar_checksum(paxHeader);
668        send_tarfile_chunk(writer, paxHeader, 512);
669
670        // Now write the pax data itself
671        int paxblocks = (paxLen + 511) / 512;
672        send_tarfile_chunk(writer, paxData, 512 * paxblocks);
673    }
674
675    // Checksum and write the 512-byte ustar file header block to the output
676    calc_tar_checksum(buf);
677    send_tarfile_chunk(writer, buf, 512);
678
679    // Now write the file data itself, for real files.  We honor tar's convention that
680    // only full 512-byte blocks are sent to write().
681    if (!isdir) {
682        off64_t toWrite = s.st_size;
683        while (toWrite > 0) {
684            size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE;
685            ssize_t nRead = read(fd, buf, toRead);
686            if (nRead < 0) {
687                err = errno;
688                ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(),
689                        err, strerror(err));
690                break;
691            } else if (nRead == 0) {
692                ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite,
693                        filepath.string());
694                err = EIO;
695                break;
696            }
697
698            // At EOF we might have a short block; NUL-pad that to a 512-byte multiple.  This
699            // depends on the OS guarantee that for ordinary files, read() will never return
700            // less than the number of bytes requested.
701            ssize_t partial = (nRead+512) % 512;
702            if (partial > 0) {
703                ssize_t remainder = 512 - partial;
704                memset(buf + nRead, 0, remainder);
705                nRead += remainder;
706            }
707            send_tarfile_chunk(writer, buf, nRead);
708            toWrite -= nRead;
709        }
710    }
711
712cleanup:
713    free(buf);
714done:
715    close(fd);
716    return err;
717}
718// end tarfile
719
720
721
722#define RESTORE_BUF_SIZE (8*1024)
723
724RestoreHelperBase::RestoreHelperBase()
725{
726    m_buf = malloc(RESTORE_BUF_SIZE);
727    m_loggedUnknownMetadata = false;
728}
729
730RestoreHelperBase::~RestoreHelperBase()
731{
732    free(m_buf);
733}
734
735status_t
736RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
737{
738    ssize_t err;
739    size_t dataSize;
740    String8 key;
741    int fd;
742    void* buf = m_buf;
743    ssize_t amt;
744    int mode;
745    int crc;
746    struct stat st;
747    FileRec r;
748
749    err = in->ReadEntityHeader(&key, &dataSize);
750    if (err != NO_ERROR) {
751        return err;
752    }
753
754    // Get the metadata block off the head of the file entity and use that to
755    // set up the output file
756    file_metadata_v1 metadata;
757    amt = in->ReadEntityData(&metadata, sizeof(metadata));
758    if (amt != sizeof(metadata)) {
759        ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
760                (long)amt, strerror(errno));
761        return EIO;
762    }
763    metadata.version = fromlel(metadata.version);
764    metadata.mode = fromlel(metadata.mode);
765    if (metadata.version > CURRENT_METADATA_VERSION) {
766        if (!m_loggedUnknownMetadata) {
767            m_loggedUnknownMetadata = true;
768            ALOGW("Restoring file with unsupported metadata version %d (currently %d)",
769                    metadata.version, CURRENT_METADATA_VERSION);
770        }
771    }
772    mode = metadata.mode;
773
774    // Write the file and compute the crc
775    crc = crc32(0L, Z_NULL, 0);
776    fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
777    if (fd == -1) {
778        ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
779        return errno;
780    }
781
782    while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
783        err = write(fd, buf, amt);
784        if (err != amt) {
785            close(fd);
786            ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
787            return errno;
788        }
789        crc = crc32(crc, (Bytef*)buf, amt);
790    }
791
792    close(fd);
793
794    // Record for the snapshot
795    err = stat(filename.string(), &st);
796    if (err != 0) {
797        ALOGW("Error stating file that we just created %s", filename.string());
798        return errno;
799    }
800
801    r.file = filename;
802    r.deleted = false;
803    r.s.modTime_sec = st.st_mtime;
804    r.s.modTime_nsec = 0; // workaround sim breakage
805    //r.s.modTime_nsec = st.st_mtime_nsec;
806    r.s.mode = st.st_mode;
807    r.s.size = st.st_size;
808    r.s.crc32 = crc;
809
810    m_files.add(key, r);
811
812    return NO_ERROR;
813}
814
815status_t
816RestoreHelperBase::WriteSnapshot(int fd)
817{
818    return write_snapshot_file(fd, m_files);;
819}
820
821#if TEST_BACKUP_HELPERS
822
823#define SCRATCH_DIR "/data/backup_helper_test/"
824
825static int
826write_text_file(const char* path, const char* data)
827{
828    int amt;
829    int fd;
830    int len;
831
832    fd = creat(path, 0666);
833    if (fd == -1) {
834        fprintf(stderr, "creat %s failed\n", path);
835        return errno;
836    }
837
838    len = strlen(data);
839    amt = write(fd, data, len);
840    if (amt != len) {
841        fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
842        return errno;
843    }
844
845    close(fd);
846
847    return 0;
848}
849
850static int
851compare_file(const char* path, const unsigned char* data, int len)
852{
853    int fd;
854    int amt;
855
856    fd = open(path, O_RDONLY);
857    if (fd == -1) {
858        fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
859        return errno;
860    }
861
862    unsigned char* contents = (unsigned char*)malloc(len);
863    if (contents == NULL) {
864        fprintf(stderr, "malloc(%d) failed\n", len);
865        return ENOMEM;
866    }
867
868    bool sizesMatch = true;
869    amt = lseek(fd, 0, SEEK_END);
870    if (amt != len) {
871        fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
872        sizesMatch = false;
873    }
874    lseek(fd, 0, SEEK_SET);
875
876    int readLen = amt < len ? amt : len;
877    amt = read(fd, contents, readLen);
878    if (amt != readLen) {
879        fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
880    }
881
882    bool contentsMatch = true;
883    for (int i=0; i<readLen; i++) {
884        if (data[i] != contents[i]) {
885            if (contentsMatch) {
886                fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
887                contentsMatch = false;
888            }
889            fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
890        }
891    }
892
893    free(contents);
894    return contentsMatch && sizesMatch ? 0 : 1;
895}
896
897int
898backup_helper_test_empty()
899{
900    int err;
901    int fd;
902    KeyedVector<String8,FileRec> snapshot;
903    const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
904
905    system("rm -r " SCRATCH_DIR);
906    mkdir(SCRATCH_DIR, 0777);
907
908    // write
909    fd = creat(filename, 0666);
910    if (fd == -1) {
911        fprintf(stderr, "error creating %s\n", filename);
912        return 1;
913    }
914
915    err = write_snapshot_file(fd, snapshot);
916
917    close(fd);
918
919    if (err != 0) {
920        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
921        return err;
922    }
923
924    static const unsigned char correct_data[] = {
925        0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
926        0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
927    };
928
929    err = compare_file(filename, correct_data, sizeof(correct_data));
930    if (err != 0) {
931        return err;
932    }
933
934    // read
935    fd = open(filename, O_RDONLY);
936    if (fd == -1) {
937        fprintf(stderr, "error opening for read %s\n", filename);
938        return 1;
939    }
940
941    KeyedVector<String8,FileState> readSnapshot;
942    err = read_snapshot_file(fd, &readSnapshot);
943    if (err != 0) {
944        fprintf(stderr, "read_snapshot_file failed %d\n", err);
945        return err;
946    }
947
948    if (readSnapshot.size() != 0) {
949        fprintf(stderr, "readSnapshot should be length 0\n");
950        return 1;
951    }
952
953    return 0;
954}
955
956int
957backup_helper_test_four()
958{
959    int err;
960    int fd;
961    KeyedVector<String8,FileRec> snapshot;
962    const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
963
964    system("rm -r " SCRATCH_DIR);
965    mkdir(SCRATCH_DIR, 0777);
966
967    // write
968    fd = creat(filename, 0666);
969    if (fd == -1) {
970        fprintf(stderr, "error opening %s\n", filename);
971        return 1;
972    }
973
974    String8 filenames[4];
975    FileState states[4];
976    FileRec r;
977    r.deleted = false;
978
979    states[0].modTime_sec = 0xfedcba98;
980    states[0].modTime_nsec = 0xdeadbeef;
981    states[0].mode = 0777; // decimal 511, hex 0x000001ff
982    states[0].size = 0xababbcbc;
983    states[0].crc32 = 0x12345678;
984    states[0].nameLen = -12;
985    r.s = states[0];
986    filenames[0] = String8("bytes_of_padding");
987    snapshot.add(filenames[0], r);
988
989    states[1].modTime_sec = 0x93400031;
990    states[1].modTime_nsec = 0xdeadbeef;
991    states[1].mode = 0666; // decimal 438, hex 0x000001b6
992    states[1].size = 0x88557766;
993    states[1].crc32 = 0x22334422;
994    states[1].nameLen = -1;
995    r.s = states[1];
996    filenames[1] = String8("bytes_of_padding3");
997    snapshot.add(filenames[1], r);
998
999    states[2].modTime_sec = 0x33221144;
1000    states[2].modTime_nsec = 0xdeadbeef;
1001    states[2].mode = 0744; // decimal 484, hex 0x000001e4
1002    states[2].size = 0x11223344;
1003    states[2].crc32 = 0x01122334;
1004    states[2].nameLen = 0;
1005    r.s = states[2];
1006    filenames[2] = String8("bytes_of_padding_2");
1007    snapshot.add(filenames[2], r);
1008
1009    states[3].modTime_sec = 0x33221144;
1010    states[3].modTime_nsec = 0xdeadbeef;
1011    states[3].mode = 0755; // decimal 493, hex 0x000001ed
1012    states[3].size = 0x11223344;
1013    states[3].crc32 = 0x01122334;
1014    states[3].nameLen = 0;
1015    r.s = states[3];
1016    filenames[3] = String8("bytes_of_padding__1");
1017    snapshot.add(filenames[3], r);
1018
1019    err = write_snapshot_file(fd, snapshot);
1020
1021    close(fd);
1022
1023    if (err != 0) {
1024        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
1025        return err;
1026    }
1027
1028    static const unsigned char correct_data[] = {
1029        // header
1030        0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
1031        0x46, 0x69, 0x6c, 0x65,  0xbc, 0x00, 0x00, 0x00,
1032
1033        // bytes_of_padding
1034        0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
1035        0xff, 0x01, 0x00, 0x00,  0xbc, 0xbc, 0xab, 0xab,
1036        0x78, 0x56, 0x34, 0x12,  0x10, 0x00, 0x00, 0x00,
1037        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1038        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1039
1040        // bytes_of_padding3
1041        0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
1042        0xb6, 0x01, 0x00, 0x00,  0x66, 0x77, 0x55, 0x88,
1043        0x22, 0x44, 0x33, 0x22,  0x11, 0x00, 0x00, 0x00,
1044        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1045        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1046        0x33, 0xab, 0xab, 0xab,
1047
1048        // bytes of padding2
1049        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
1050        0xe4, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
1051        0x34, 0x23, 0x12, 0x01,  0x12, 0x00, 0x00, 0x00,
1052        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1053        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1054        0x5f, 0x32, 0xab, 0xab,
1055
1056        // bytes of padding3
1057        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
1058        0xed, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
1059        0x34, 0x23, 0x12, 0x01,  0x13, 0x00, 0x00, 0x00,
1060        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1061        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1062        0x5f, 0x5f, 0x31, 0xab
1063    };
1064
1065    err = compare_file(filename, correct_data, sizeof(correct_data));
1066    if (err != 0) {
1067        return err;
1068    }
1069
1070    // read
1071    fd = open(filename, O_RDONLY);
1072    if (fd == -1) {
1073        fprintf(stderr, "error opening for read %s\n", filename);
1074        return 1;
1075    }
1076
1077
1078    KeyedVector<String8,FileState> readSnapshot;
1079    err = read_snapshot_file(fd, &readSnapshot);
1080    if (err != 0) {
1081        fprintf(stderr, "read_snapshot_file failed %d\n", err);
1082        return err;
1083    }
1084
1085    if (readSnapshot.size() != 4) {
1086        fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
1087        return 1;
1088    }
1089
1090    bool matched = true;
1091    for (size_t i=0; i<readSnapshot.size(); i++) {
1092        const String8& name = readSnapshot.keyAt(i);
1093        const FileState state = readSnapshot.valueAt(i);
1094
1095        if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
1096                || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
1097                || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
1098            fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n"
1099                            "          actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i,
1100                    states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
1101                    states[i].crc32, name.length(), filenames[i].string(),
1102                    state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
1103                    state.nameLen, name.string());
1104            matched = false;
1105        }
1106    }
1107
1108    return matched ? 0 : 1;
1109}
1110
1111// hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
1112const unsigned char DATA_GOLDEN_FILE[] = {
1113     0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
1114     0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
1115     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
1116     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
1117     0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
1118     0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
1119     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1120     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
1121     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1122     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
1123     0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
1124     0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
1125     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
1126     0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
1127     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
1128     0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
1129     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
1130     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1131     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
1132     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00
1133
1134};
1135const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
1136
1137static int
1138test_write_header_and_entity(BackupDataWriter& writer, const char* str)
1139{
1140    int err;
1141    String8 text(str);
1142
1143    err = writer.WriteEntityHeader(text, text.length()+1);
1144    if (err != 0) {
1145        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
1146        return err;
1147    }
1148
1149    err = writer.WriteEntityData(text.string(), text.length()+1);
1150    if (err != 0) {
1151        fprintf(stderr, "write failed for data '%s'\n", text.string());
1152        return errno;
1153    }
1154
1155    return err;
1156}
1157
1158int
1159backup_helper_test_data_writer()
1160{
1161    int err;
1162    int fd;
1163    const char* filename = SCRATCH_DIR "data_writer.data";
1164
1165    system("rm -r " SCRATCH_DIR);
1166    mkdir(SCRATCH_DIR, 0777);
1167    mkdir(SCRATCH_DIR "data", 0777);
1168
1169    fd = creat(filename, 0666);
1170    if (fd == -1) {
1171        fprintf(stderr, "error creating: %s\n", strerror(errno));
1172        return errno;
1173    }
1174
1175    BackupDataWriter writer(fd);
1176
1177    err = 0;
1178    err |= test_write_header_and_entity(writer, "no_padding_");
1179    err |= test_write_header_and_entity(writer, "padded_to__3");
1180    err |= test_write_header_and_entity(writer, "padded_to_2__");
1181    err |= test_write_header_and_entity(writer, "padded_to1");
1182
1183    close(fd);
1184
1185    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1186    if (err != 0) {
1187        return err;
1188    }
1189
1190    return err;
1191}
1192
1193int
1194test_read_header_and_entity(BackupDataReader& reader, const char* str)
1195{
1196    int err;
1197    int bufSize = strlen(str)+1;
1198    char* buf = (char*)malloc(bufSize);
1199    String8 string;
1200    int cookie = 0x11111111;
1201    size_t actualSize;
1202    bool done;
1203    int type;
1204    ssize_t nRead;
1205
1206    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
1207
1208    err = reader.ReadNextHeader(&done, &type);
1209    if (done) {
1210        fprintf(stderr, "should not be done yet\n");
1211        goto finished;
1212    }
1213    if (err != 0) {
1214        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
1215        goto finished;
1216    }
1217    if (type != BACKUP_HEADER_ENTITY_V1) {
1218        err = EINVAL;
1219        fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
1220    }
1221
1222    err = reader.ReadEntityHeader(&string, &actualSize);
1223    if (err != 0) {
1224        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
1225        goto finished;
1226    }
1227    if (string != str) {
1228        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
1229        err = EINVAL;
1230        goto finished;
1231    }
1232    if ((int)actualSize != bufSize) {
1233        fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
1234                actualSize);
1235        err = EINVAL;
1236        goto finished;
1237    }
1238
1239    nRead = reader.ReadEntityData(buf, bufSize);
1240    if (nRead < 0) {
1241        err = reader.Status();
1242        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
1243        goto finished;
1244    }
1245
1246    if (0 != memcmp(buf, str, bufSize)) {
1247        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
1248                "%02x %02x %02x %02x  '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
1249                buf[0], buf[1], buf[2], buf[3]);
1250        err = EINVAL;
1251        goto finished;
1252    }
1253
1254    // The next read will confirm whether it got the right amount of data.
1255
1256finished:
1257    if (err != NO_ERROR) {
1258        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
1259    }
1260    free(buf);
1261    return err;
1262}
1263
1264int
1265backup_helper_test_data_reader()
1266{
1267    int err;
1268    int fd;
1269    const char* filename = SCRATCH_DIR "data_reader.data";
1270
1271    system("rm -r " SCRATCH_DIR);
1272    mkdir(SCRATCH_DIR, 0777);
1273    mkdir(SCRATCH_DIR "data", 0777);
1274
1275    fd = creat(filename, 0666);
1276    if (fd == -1) {
1277        fprintf(stderr, "error creating: %s\n", strerror(errno));
1278        return errno;
1279    }
1280
1281    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1282    if (err != DATA_GOLDEN_FILE_SIZE) {
1283        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
1284        return errno;
1285    }
1286
1287    close(fd);
1288
1289    fd = open(filename, O_RDONLY);
1290    if (fd == -1) {
1291        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
1292                filename);
1293        return errno;
1294    }
1295
1296    {
1297        BackupDataReader reader(fd);
1298
1299        err = 0;
1300
1301        if (err == NO_ERROR) {
1302            err = test_read_header_and_entity(reader, "no_padding_");
1303        }
1304
1305        if (err == NO_ERROR) {
1306            err = test_read_header_and_entity(reader, "padded_to__3");
1307        }
1308
1309        if (err == NO_ERROR) {
1310            err = test_read_header_and_entity(reader, "padded_to_2__");
1311        }
1312
1313        if (err == NO_ERROR) {
1314            err = test_read_header_and_entity(reader, "padded_to1");
1315        }
1316    }
1317
1318    close(fd);
1319
1320    return err;
1321}
1322
1323static int
1324get_mod_time(const char* filename, struct timeval times[2])
1325{
1326    int err;
1327    struct stat64 st;
1328    err = stat64(filename, &st);
1329    if (err != 0) {
1330        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
1331        return errno;
1332    }
1333    times[0].tv_sec = st.st_atime;
1334    times[1].tv_sec = st.st_mtime;
1335
1336    // If st_atime is a macro then struct stat64 uses struct timespec
1337    // to store the access and modif time values and typically
1338    // st_*time_nsec is not defined. In glibc, this is controlled by
1339    // __USE_MISC.
1340#ifdef __USE_MISC
1341#if !defined(st_atime) || defined(st_atime_nsec)
1342#error "Check if this __USE_MISC conditional is still needed."
1343#endif
1344    times[0].tv_usec = st.st_atim.tv_nsec / 1000;
1345    times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1346#else
1347    times[0].tv_usec = st.st_atime_nsec / 1000;
1348    times[1].tv_usec = st.st_mtime_nsec / 1000;
1349#endif
1350
1351    return 0;
1352}
1353
1354int
1355backup_helper_test_files()
1356{
1357    int err;
1358    int oldSnapshotFD;
1359    int dataStreamFD;
1360    int newSnapshotFD;
1361
1362    system("rm -r " SCRATCH_DIR);
1363    mkdir(SCRATCH_DIR, 0777);
1364    mkdir(SCRATCH_DIR "data", 0777);
1365
1366    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1367    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1368    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
1369    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
1370    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
1371    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
1372
1373    char const* files_before[] = {
1374        SCRATCH_DIR "data/b",
1375        SCRATCH_DIR "data/c",
1376        SCRATCH_DIR "data/d",
1377        SCRATCH_DIR "data/e",
1378        SCRATCH_DIR "data/f"
1379    };
1380
1381    char const* keys_before[] = {
1382        "data/b",
1383        "data/c",
1384        "data/d",
1385        "data/e",
1386        "data/f"
1387    };
1388
1389    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
1390    if (dataStreamFD == -1) {
1391        fprintf(stderr, "error creating: %s\n", strerror(errno));
1392        return errno;
1393    }
1394
1395    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
1396    if (newSnapshotFD == -1) {
1397        fprintf(stderr, "error creating: %s\n", strerror(errno));
1398        return errno;
1399    }
1400
1401    {
1402        BackupDataWriter dataStream(dataStreamFD);
1403
1404        err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
1405        if (err != 0) {
1406            return err;
1407        }
1408    }
1409
1410    close(dataStreamFD);
1411    close(newSnapshotFD);
1412
1413    sleep(3);
1414
1415    struct timeval d_times[2];
1416    struct timeval e_times[2];
1417
1418    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
1419    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
1420    if (err != 0) {
1421        return err;
1422    }
1423
1424    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1425    unlink(SCRATCH_DIR "data/c");
1426    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1427    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
1428    utimes(SCRATCH_DIR "data/d", d_times);
1429    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
1430    utimes(SCRATCH_DIR "data/e", e_times);
1431    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
1432    unlink(SCRATCH_DIR "data/f");
1433
1434    char const* files_after[] = {
1435        SCRATCH_DIR "data/a", // added
1436        SCRATCH_DIR "data/b", // same
1437        SCRATCH_DIR "data/c", // different mod time
1438        SCRATCH_DIR "data/d", // different size (same mod time)
1439        SCRATCH_DIR "data/e", // different contents (same mod time, same size)
1440        SCRATCH_DIR "data/g"  // added
1441    };
1442
1443    char const* keys_after[] = {
1444        "data/a", // added
1445        "data/b", // same
1446        "data/c", // different mod time
1447        "data/d", // different size (same mod time)
1448        "data/e", // different contents (same mod time, same size)
1449        "data/g"  // added
1450    };
1451
1452    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
1453    if (oldSnapshotFD == -1) {
1454        fprintf(stderr, "error opening: %s\n", strerror(errno));
1455        return errno;
1456    }
1457
1458    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
1459    if (dataStreamFD == -1) {
1460        fprintf(stderr, "error creating: %s\n", strerror(errno));
1461        return errno;
1462    }
1463
1464    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
1465    if (newSnapshotFD == -1) {
1466        fprintf(stderr, "error creating: %s\n", strerror(errno));
1467        return errno;
1468    }
1469
1470    {
1471        BackupDataWriter dataStream(dataStreamFD);
1472
1473        err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
1474        if (err != 0) {
1475            return err;
1476        }
1477}
1478
1479    close(oldSnapshotFD);
1480    close(dataStreamFD);
1481    close(newSnapshotFD);
1482
1483    return 0;
1484}
1485
1486int
1487backup_helper_test_null_base()
1488{
1489    int err;
1490    int oldSnapshotFD;
1491    int dataStreamFD;
1492    int newSnapshotFD;
1493
1494    system("rm -r " SCRATCH_DIR);
1495    mkdir(SCRATCH_DIR, 0777);
1496    mkdir(SCRATCH_DIR "data", 0777);
1497
1498    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1499
1500    char const* files[] = {
1501        SCRATCH_DIR "data/a",
1502    };
1503
1504    char const* keys[] = {
1505        "a",
1506    };
1507
1508    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1509    if (dataStreamFD == -1) {
1510        fprintf(stderr, "error creating: %s\n", strerror(errno));
1511        return errno;
1512    }
1513
1514    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1515    if (newSnapshotFD == -1) {
1516        fprintf(stderr, "error creating: %s\n", strerror(errno));
1517        return errno;
1518    }
1519
1520    {
1521        BackupDataWriter dataStream(dataStreamFD);
1522
1523        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1524        if (err != 0) {
1525            return err;
1526        }
1527    }
1528
1529    close(dataStreamFD);
1530    close(newSnapshotFD);
1531
1532    return 0;
1533}
1534
1535int
1536backup_helper_test_missing_file()
1537{
1538    int err;
1539    int oldSnapshotFD;
1540    int dataStreamFD;
1541    int newSnapshotFD;
1542
1543    system("rm -r " SCRATCH_DIR);
1544    mkdir(SCRATCH_DIR, 0777);
1545    mkdir(SCRATCH_DIR "data", 0777);
1546
1547    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1548
1549    char const* files[] = {
1550        SCRATCH_DIR "data/a",
1551        SCRATCH_DIR "data/b",
1552        SCRATCH_DIR "data/c",
1553    };
1554
1555    char const* keys[] = {
1556        "a",
1557        "b",
1558        "c",
1559    };
1560
1561    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1562    if (dataStreamFD == -1) {
1563        fprintf(stderr, "error creating: %s\n", strerror(errno));
1564        return errno;
1565    }
1566
1567    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1568    if (newSnapshotFD == -1) {
1569        fprintf(stderr, "error creating: %s\n", strerror(errno));
1570        return errno;
1571    }
1572
1573    {
1574        BackupDataWriter dataStream(dataStreamFD);
1575
1576        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1577        if (err != 0) {
1578            return err;
1579        }
1580    }
1581
1582    close(dataStreamFD);
1583    close(newSnapshotFD);
1584
1585    return 0;
1586}
1587
1588
1589#endif // TEST_BACKUP_HELPERS
1590
1591}
1592