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