BackupHelpers.cpp revision 33d0392d3e4c42a7d48e8bd1210a1525aca8c2d3
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
71static const bool kIsDebug = false;
72#if TEST_BACKUP_HELPERS
73#define LOGP(f, x...) if (kIsDebug) printf(f "\n", x)
74#else
75#define LOGP(x...) if (kIsDebug) ALOGD(x)
76#endif
77
78const static int ROUND_UP[4] = { 0, 3, 2, 1 };
79
80static inline int
81round_up(int n)
82{
83    return n + ROUND_UP[n % 4];
84}
85
86static int
87read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
88{
89    int bytesRead = 0;
90    int amt;
91    SnapshotHeader header;
92
93    amt = read(fd, &header, sizeof(header));
94    if (amt != sizeof(header)) {
95        return errno;
96    }
97    bytesRead += amt;
98
99    if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
100        ALOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
101        return 1;
102    }
103
104    for (int i=0; i<header.fileCount; i++) {
105        FileState file;
106        char filenameBuf[128];
107
108        amt = read(fd, &file, sizeof(FileState));
109        if (amt != sizeof(FileState)) {
110            ALOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
111            return 1;
112        }
113        bytesRead += amt;
114
115        // filename is not NULL terminated, but it is padded
116        int nameBufSize = round_up(file.nameLen);
117        char* filename = nameBufSize <= (int)sizeof(filenameBuf)
118                ? filenameBuf
119                : (char*)malloc(nameBufSize);
120        amt = read(fd, filename, nameBufSize);
121        if (amt == nameBufSize) {
122            snapshot->add(String8(filename, file.nameLen), file);
123        }
124        bytesRead += amt;
125        if (filename != filenameBuf) {
126            free(filename);
127        }
128        if (amt != nameBufSize) {
129            ALOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
130            return 1;
131        }
132    }
133
134    if (header.totalSize != bytesRead) {
135        ALOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
136                header.totalSize, bytesRead);
137        return 1;
138    }
139
140    return 0;
141}
142
143static int
144write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot)
145{
146    int fileCount = 0;
147    int bytesWritten = sizeof(SnapshotHeader);
148    // preflight size
149    const int N = snapshot.size();
150    for (int i=0; i<N; i++) {
151        const FileRec& g = snapshot.valueAt(i);
152        if (!g.deleted) {
153            const String8& name = snapshot.keyAt(i);
154            bytesWritten += sizeof(FileState) + round_up(name.length());
155            fileCount++;
156        }
157    }
158
159    LOGP("write_snapshot_file fd=%d\n", fd);
160
161    int amt;
162    SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten };
163
164    amt = write(fd, &header, sizeof(header));
165    if (amt != sizeof(header)) {
166        ALOGW("write_snapshot_file error writing header %s", strerror(errno));
167        return errno;
168    }
169
170    for (int i=0; i<N; i++) {
171        FileRec r = snapshot.valueAt(i);
172        if (!r.deleted) {
173            const String8& name = snapshot.keyAt(i);
174            int nameLen = r.s.nameLen = name.length();
175
176            amt = write(fd, &r.s, sizeof(FileState));
177            if (amt != sizeof(FileState)) {
178                ALOGW("write_snapshot_file error writing header %s", strerror(errno));
179                return 1;
180            }
181
182            // filename is not NULL terminated, but it is padded
183            amt = write(fd, name.string(), nameLen);
184            if (amt != nameLen) {
185                ALOGW("write_snapshot_file error writing filename %s", strerror(errno));
186                return 1;
187            }
188            int paddingLen = ROUND_UP[nameLen % 4];
189            if (paddingLen != 0) {
190                int padding = 0xabababab;
191                amt = write(fd, &padding, paddingLen);
192                if (amt != paddingLen) {
193                    ALOGW("write_snapshot_file error writing %d bytes of filename padding %s",
194                            paddingLen, strerror(errno));
195                    return 1;
196                }
197            }
198        }
199    }
200
201    return 0;
202}
203
204static int
205write_delete_file(BackupDataWriter* dataStream, const String8& key)
206{
207    LOGP("write_delete_file %s\n", key.string());
208    return dataStream->WriteEntityHeader(key, -1);
209}
210
211static int
212write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key,
213        char const* realFilename)
214{
215    LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode);
216
217    const int bufsize = 4*1024;
218    int err;
219    int amt;
220    int fileSize;
221    int bytesLeft;
222    file_metadata_v1 metadata;
223
224    char* buf = (char*)malloc(bufsize);
225
226    fileSize = lseek(fd, 0, SEEK_END);
227    lseek(fd, 0, SEEK_SET);
228
229    if (sizeof(metadata) != 16) {
230        ALOGE("ERROR: metadata block is the wrong size!");
231    }
232
233    bytesLeft = fileSize + sizeof(metadata);
234    err = dataStream->WriteEntityHeader(key, bytesLeft);
235    if (err != 0) {
236        free(buf);
237        return err;
238    }
239
240    // store the file metadata first
241    metadata.version = tolel(CURRENT_METADATA_VERSION);
242    metadata.mode = tolel(mode);
243    metadata.undefined_1 = metadata.undefined_2 = 0;
244    err = dataStream->WriteEntityData(&metadata, sizeof(metadata));
245    if (err != 0) {
246        free(buf);
247        return err;
248    }
249    bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now
250
251    // now store the file content
252    while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) {
253        bytesLeft -= amt;
254        if (bytesLeft < 0) {
255            amt += bytesLeft; // Plus a negative is minus.  Don't write more than we promised.
256        }
257        err = dataStream->WriteEntityData(buf, amt);
258        if (err != 0) {
259            free(buf);
260            return err;
261        }
262    }
263    if (bytesLeft != 0) {
264        if (bytesLeft > 0) {
265            // Pad out the space we promised in the buffer.  We can't corrupt the buffer,
266            // even though the data we're sending is probably bad.
267            memset(buf, 0, bufsize);
268            while (bytesLeft > 0) {
269                amt = bytesLeft < bufsize ? bytesLeft : bufsize;
270                bytesLeft -= amt;
271                err = dataStream->WriteEntityData(buf, amt);
272                if (err != 0) {
273                    free(buf);
274                    return err;
275                }
276            }
277        }
278        ALOGE("write_update_file size mismatch for %s. expected=%d actual=%d."
279                " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft);
280    }
281
282    free(buf);
283    return NO_ERROR;
284}
285
286static int
287write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename)
288{
289    int err;
290    struct stat st;
291
292    err = stat(realFilename, &st);
293    if (err < 0) {
294        return errno;
295    }
296
297    int fd = open(realFilename, O_RDONLY);
298    if (fd == -1) {
299        return errno;
300    }
301
302    err = write_update_file(dataStream, fd, st.st_mode, key, realFilename);
303    close(fd);
304    return err;
305}
306
307static int
308compute_crc32(const char* file, FileRec* out) {
309    int fd = open(file, O_RDONLY);
310    if (fd < 0) {
311        return -1;
312    }
313
314    const int bufsize = 4*1024;
315    int amt;
316
317    char* buf = (char*)malloc(bufsize);
318    int crc = crc32(0L, Z_NULL, 0);
319
320    lseek(fd, 0, SEEK_SET);
321
322    while ((amt = read(fd, buf, bufsize)) != 0) {
323        crc = crc32(crc, (Bytef*)buf, amt);
324    }
325
326    close(fd);
327    free(buf);
328
329    out->s.crc32 = crc;
330    return NO_ERROR;
331}
332
333int
334back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
335        char const* const* files, char const* const* keys, int fileCount)
336{
337    int err;
338    KeyedVector<String8,FileState> oldSnapshot;
339    KeyedVector<String8,FileRec> newSnapshot;
340
341    if (oldSnapshotFD != -1) {
342        err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
343        if (err != 0) {
344            // On an error, treat this as a full backup.
345            oldSnapshot.clear();
346        }
347    }
348
349    for (int i=0; i<fileCount; i++) {
350        String8 key(keys[i]);
351        FileRec r;
352        char const* file = files[i];
353        r.file = file;
354        struct stat st;
355
356        err = stat(file, &st);
357        if (err != 0) {
358            // not found => treat as deleted
359            continue;
360        } else {
361            r.deleted = false;
362            r.s.modTime_sec = st.st_mtime;
363            r.s.modTime_nsec = 0; // workaround sim breakage
364            //r.s.modTime_nsec = st.st_mtime_nsec;
365            r.s.mode = st.st_mode;
366            r.s.size = st.st_size;
367
368            if (newSnapshot.indexOfKey(key) >= 0) {
369                LOGP("back_up_files key already in use '%s'", key.string());
370                return -1;
371            }
372
373            // compute the CRC
374            if (compute_crc32(file, &r) != NO_ERROR) {
375                ALOGW("Unable to open file %s", file);
376                continue;
377            }
378        }
379        newSnapshot.add(key, r);
380    }
381
382    int n = 0;
383    int N = oldSnapshot.size();
384    int m = 0;
385    int M = newSnapshot.size();
386
387    while (n<N && m<M) {
388        const String8& p = oldSnapshot.keyAt(n);
389        const String8& q = newSnapshot.keyAt(m);
390        FileRec& g = newSnapshot.editValueAt(m);
391        int cmp = p.compare(q);
392        if (cmp < 0) {
393            // file present in oldSnapshot, but not present in newSnapshot
394            LOGP("file removed: %s", p.string());
395            write_delete_file(dataStream, p);
396            n++;
397        } else if (cmp > 0) {
398            // file added
399            LOGP("file added: %s crc=0x%08x", g.file.string(), g.s.crc32);
400            write_update_file(dataStream, q, g.file.string());
401            m++;
402        } else {
403            // same file exists in both old and new; check whether to update
404            const FileState& f = oldSnapshot.valueAt(n);
405
406            LOGP("%s", q.string());
407            LOGP("  old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
408                    f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32);
409            LOGP("  new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
410                    g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32);
411            if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
412                    || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) {
413                int fd = open(g.file.string(), O_RDONLY);
414                if (fd < 0) {
415                    ALOGE("Unable to read file for backup: %s", g.file.string());
416                } else {
417                    write_update_file(dataStream, fd, g.s.mode, p, g.file.string());
418                    close(fd);
419                }
420            }
421            n++;
422            m++;
423        }
424    }
425
426    // these were deleted
427    while (n<N) {
428        write_delete_file(dataStream, oldSnapshot.keyAt(n));
429        n++;
430    }
431
432    // these were added
433    while (m<M) {
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
445static void calc_tar_checksum(char* buf) {
446    // [ 148 :   8 ] checksum -- to be calculated with this field as space chars
447    memset(buf + 148, ' ', 8);
448
449    uint16_t sum = 0;
450    for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) {
451        sum += *p;
452    }
453
454    // Now write the real checksum value:
455    // [ 148 :   8 ]  checksum: 6 octal digits [leading zeroes], NUL, SPC
456    sprintf(buf + 148, "%06o", sum); // the trailing space is already in place
457}
458
459// Returns number of bytes written
460static int write_pax_header_entry(char* buf, const char* key, const char* value) {
461    // start with the size of "1 key=value\n"
462    int len = strlen(key) + strlen(value) + 4;
463    if (len > 9) len++;
464    if (len > 99) len++;
465    if (len > 999) len++;
466    // since PATH_MAX is 4096 we don't expect to have to generate any single
467    // header entry longer than 9999 characters
468
469    return sprintf(buf, "%d %s=%s\n", len, key, value);
470}
471
472// Wire format to the backup manager service is chunked:  each chunk is prefixed by
473// a 4-byte count of its size.  A chunk size of zero (four zero bytes) indicates EOD.
474void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) {
475    uint32_t chunk_size_no = htonl(size);
476    writer->WriteEntityData(&chunk_size_no, 4);
477    if (size != 0) writer->WriteEntityData(buffer, size);
478}
479
480int write_tarfile(const String8& packageName, const String8& domain,
481        const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
482{
483    // In the output stream everything is stored relative to the root
484    const char* relstart = filepath.string() + rootpath.length();
485    if (*relstart == '/') relstart++;     // won't be true when path == rootpath
486    String8 relpath(relstart);
487
488    // If relpath is empty, it means this is the top of one of the standard named
489    // domain directories, so we should just skip it
490    if (relpath.length() == 0) {
491        return 0;
492    }
493
494    // Too long a name for the ustar format?
495    //    "apps/" + packagename + '/' + domainpath < 155 chars
496    //    relpath < 100 chars
497    bool needExtended = false;
498    if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) {
499        needExtended = true;
500    }
501
502    // Non-7bit-clean path also means needing pax extended format
503    if (!needExtended) {
504        for (size_t i = 0; i < filepath.length(); i++) {
505            if ((filepath[i] & 0x80) != 0) {
506                needExtended = true;
507                break;
508            }
509        }
510    }
511
512    int err = 0;
513    struct stat64 s;
514    if (lstat64(filepath.string(), &s) != 0) {
515        err = errno;
516        ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
517        return err;
518    }
519
520    String8 fullname;   // for pax later on
521    String8 prefix;
522
523    const int isdir = S_ISDIR(s.st_mode);
524    if (isdir) s.st_size = 0;   // directories get no actual data in the tar stream
525
526    // !!! TODO: use mmap when possible to avoid churning the buffer cache
527    // !!! TODO: this will break with symlinks; need to use readlink(2)
528    int fd = open(filepath.string(), O_RDONLY);
529    if (fd < 0) {
530        err = errno;
531        ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
532        return err;
533    }
534
535    // read/write up to this much at a time.
536    const size_t BUFSIZE = 32 * 1024;
537    char* buf = (char *)calloc(1,BUFSIZE);
538    char* paxHeader = buf + 512;    // use a different chunk of it as separate scratch
539    char* paxData = buf + 1024;
540
541    if (buf == NULL) {
542        ALOGE("Out of mem allocating transfer buffer");
543        err = ENOMEM;
544        goto done;
545    }
546
547    // Magic fields for the ustar file format
548    strcat(buf + 257, "ustar");
549    strcat(buf + 263, "00");
550
551    // [ 265 : 32 ] user name, ignored on restore
552    // [ 297 : 32 ] group name, ignored on restore
553
554    // [ 100 :   8 ] file mode
555    snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT);
556
557    // [ 108 :   8 ] uid -- ignored in Android format; uids are remapped at restore time
558    // [ 116 :   8 ] gid -- ignored in Android format
559    snprintf(buf + 108, 8, "0%lo", (unsigned long)s.st_uid);
560    snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
561
562    // [ 124 :  12 ] file size in bytes
563    if (s.st_size > 077777777777LL) {
564        // very large files need a pax extended size header
565        needExtended = true;
566    }
567    snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
568
569    // [ 136 :  12 ] last mod time as a UTC time_t
570    snprintf(buf + 136, 12, "%0lo", (unsigned long)s.st_mtime);
571
572    // [ 156 :   1 ] link/file type
573    uint8_t type;
574    if (isdir) {
575        type = '5';     // tar magic: '5' == directory
576    } else if (S_ISREG(s.st_mode)) {
577        type = '0';     // tar magic: '0' == normal file
578    } else {
579        ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
580        goto cleanup;
581    }
582    buf[156] = type;
583
584    // [ 157 : 100 ] name of linked file [not implemented]
585
586    {
587        // Prefix and main relative path.  Path lengths have been preflighted.
588        if (packageName.length() > 0) {
589            prefix = "apps/";
590            prefix += packageName;
591        }
592        if (domain.length() > 0) {
593            prefix.appendPath(domain);
594        }
595
596        // pax extended means we don't put in a prefix field, and put a different
597        // string in the basic name field.  We can also construct the full path name
598        // out of the substrings we've now built.
599        fullname = prefix;
600        fullname.appendPath(relpath);
601
602        // ustar:
603        //    [   0 : 100 ]; file name/path
604        //    [ 345 : 155 ] filename path prefix
605        // We only use the prefix area if fullname won't fit in the path
606        if (fullname.length() > 100) {
607            strncpy(buf, relpath.string(), 100);
608            strncpy(buf + 345, prefix.string(), 155);
609        } else {
610            strncpy(buf, fullname.string(), 100);
611        }
612    }
613
614    // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used
615
616    ALOGI("   Name: %s", fullname.string());
617
618    // If we're using a pax extended header, build & write that here; lengths are
619    // already preflighted
620    if (needExtended) {
621        char sizeStr[32];   // big enough for a 64-bit unsigned value in decimal
622        char* p = paxData;
623
624        // construct the pax extended header data block
625        memset(paxData, 0, BUFSIZE - (paxData - buf));
626
627        // size header -- calc len in digits by actually rendering the number
628        // to a string - brute force but simple
629        snprintf(sizeStr, sizeof(sizeStr), "%lld", (long long)s.st_size);
630        p += write_pax_header_entry(p, "size", sizeStr);
631
632        // fullname was generated above with the ustar paths
633        p += write_pax_header_entry(p, "path", fullname.string());
634
635        // Now we know how big the pax data is
636        int paxLen = p - paxData;
637
638        // Now build the pax *header* templated on the ustar header
639        memcpy(paxHeader, buf, 512);
640
641        String8 leaf = fullname.getPathLeaf();
642        memset(paxHeader, 0, 100);                  // rewrite the name area
643        snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string());
644        memset(paxHeader + 345, 0, 155);            // rewrite the prefix area
645        strncpy(paxHeader + 345, prefix.string(), 155);
646
647        paxHeader[156] = 'x';                       // mark it as a pax extended header
648
649        // [ 124 :  12 ] size of pax extended header data
650        memset(paxHeader + 124, 0, 12);
651        snprintf(paxHeader + 124, 12, "%011o", (unsigned int)(p - paxData));
652
653        // Checksum and write the pax block header
654        calc_tar_checksum(paxHeader);
655        send_tarfile_chunk(writer, paxHeader, 512);
656
657        // Now write the pax data itself
658        int paxblocks = (paxLen + 511) / 512;
659        send_tarfile_chunk(writer, paxData, 512 * paxblocks);
660    }
661
662    // Checksum and write the 512-byte ustar file header block to the output
663    calc_tar_checksum(buf);
664    send_tarfile_chunk(writer, buf, 512);
665
666    // Now write the file data itself, for real files.  We honor tar's convention that
667    // only full 512-byte blocks are sent to write().
668    if (!isdir) {
669        off64_t toWrite = s.st_size;
670        while (toWrite > 0) {
671            size_t toRead = toWrite;
672            if (toRead > BUFSIZE) {
673                toRead = BUFSIZE;
674            }
675            ssize_t nRead = read(fd, buf, toRead);
676            if (nRead < 0) {
677                err = errno;
678                ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(),
679                        err, strerror(err));
680                break;
681            } else if (nRead == 0) {
682                ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite,
683                        filepath.string());
684                err = EIO;
685                break;
686            }
687
688            // At EOF we might have a short block; NUL-pad that to a 512-byte multiple.  This
689            // depends on the OS guarantee that for ordinary files, read() will never return
690            // less than the number of bytes requested.
691            ssize_t partial = (nRead+512) % 512;
692            if (partial > 0) {
693                ssize_t remainder = 512 - partial;
694                memset(buf + nRead, 0, remainder);
695                nRead += remainder;
696            }
697            send_tarfile_chunk(writer, buf, nRead);
698            toWrite -= nRead;
699        }
700    }
701
702cleanup:
703    free(buf);
704done:
705    close(fd);
706    return err;
707}
708// end tarfile
709
710
711
712#define RESTORE_BUF_SIZE (8*1024)
713
714RestoreHelperBase::RestoreHelperBase()
715{
716    m_buf = malloc(RESTORE_BUF_SIZE);
717    m_loggedUnknownMetadata = false;
718}
719
720RestoreHelperBase::~RestoreHelperBase()
721{
722    free(m_buf);
723}
724
725status_t
726RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
727{
728    ssize_t err;
729    size_t dataSize;
730    String8 key;
731    int fd;
732    void* buf = m_buf;
733    ssize_t amt;
734    int mode;
735    int crc;
736    struct stat st;
737    FileRec r;
738
739    err = in->ReadEntityHeader(&key, &dataSize);
740    if (err != NO_ERROR) {
741        return err;
742    }
743
744    // Get the metadata block off the head of the file entity and use that to
745    // set up the output file
746    file_metadata_v1 metadata;
747    amt = in->ReadEntityData(&metadata, sizeof(metadata));
748    if (amt != sizeof(metadata)) {
749        ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
750                (long)amt, strerror(errno));
751        return EIO;
752    }
753    metadata.version = fromlel(metadata.version);
754    metadata.mode = fromlel(metadata.mode);
755    if (metadata.version > CURRENT_METADATA_VERSION) {
756        if (!m_loggedUnknownMetadata) {
757            m_loggedUnknownMetadata = true;
758            ALOGW("Restoring file with unsupported metadata version %d (currently %d)",
759                    metadata.version, CURRENT_METADATA_VERSION);
760        }
761    }
762    mode = metadata.mode;
763
764    // Write the file and compute the crc
765    crc = crc32(0L, Z_NULL, 0);
766    fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
767    if (fd == -1) {
768        ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
769        return errno;
770    }
771
772    while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
773        err = write(fd, buf, amt);
774        if (err != amt) {
775            close(fd);
776            ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
777            return errno;
778        }
779        crc = crc32(crc, (Bytef*)buf, amt);
780    }
781
782    close(fd);
783
784    // Record for the snapshot
785    err = stat(filename.string(), &st);
786    if (err != 0) {
787        ALOGW("Error stating file that we just created %s", filename.string());
788        return errno;
789    }
790
791    r.file = filename;
792    r.deleted = false;
793    r.s.modTime_sec = st.st_mtime;
794    r.s.modTime_nsec = 0; // workaround sim breakage
795    //r.s.modTime_nsec = st.st_mtime_nsec;
796    r.s.mode = st.st_mode;
797    r.s.size = st.st_size;
798    r.s.crc32 = crc;
799
800    m_files.add(key, r);
801
802    return NO_ERROR;
803}
804
805status_t
806RestoreHelperBase::WriteSnapshot(int fd)
807{
808    return write_snapshot_file(fd, m_files);;
809}
810
811#if TEST_BACKUP_HELPERS
812
813#define SCRATCH_DIR "/data/backup_helper_test/"
814
815static int
816write_text_file(const char* path, const char* data)
817{
818    int amt;
819    int fd;
820    int len;
821
822    fd = creat(path, 0666);
823    if (fd == -1) {
824        fprintf(stderr, "creat %s failed\n", path);
825        return errno;
826    }
827
828    len = strlen(data);
829    amt = write(fd, data, len);
830    if (amt != len) {
831        fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
832        return errno;
833    }
834
835    close(fd);
836
837    return 0;
838}
839
840static int
841compare_file(const char* path, const unsigned char* data, int len)
842{
843    int fd;
844    int amt;
845
846    fd = open(path, O_RDONLY);
847    if (fd == -1) {
848        fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
849        return errno;
850    }
851
852    unsigned char* contents = (unsigned char*)malloc(len);
853    if (contents == NULL) {
854        fprintf(stderr, "malloc(%d) failed\n", len);
855        return ENOMEM;
856    }
857
858    bool sizesMatch = true;
859    amt = lseek(fd, 0, SEEK_END);
860    if (amt != len) {
861        fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
862        sizesMatch = false;
863    }
864    lseek(fd, 0, SEEK_SET);
865
866    int readLen = amt < len ? amt : len;
867    amt = read(fd, contents, readLen);
868    if (amt != readLen) {
869        fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
870    }
871
872    bool contentsMatch = true;
873    for (int i=0; i<readLen; i++) {
874        if (data[i] != contents[i]) {
875            if (contentsMatch) {
876                fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
877                contentsMatch = false;
878            }
879            fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
880        }
881    }
882
883    free(contents);
884    return contentsMatch && sizesMatch ? 0 : 1;
885}
886
887int
888backup_helper_test_empty()
889{
890    int err;
891    int fd;
892    KeyedVector<String8,FileRec> snapshot;
893    const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
894
895    system("rm -r " SCRATCH_DIR);
896    mkdir(SCRATCH_DIR, 0777);
897
898    // write
899    fd = creat(filename, 0666);
900    if (fd == -1) {
901        fprintf(stderr, "error creating %s\n", filename);
902        return 1;
903    }
904
905    err = write_snapshot_file(fd, snapshot);
906
907    close(fd);
908
909    if (err != 0) {
910        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
911        return err;
912    }
913
914    static const unsigned char correct_data[] = {
915        0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
916        0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
917    };
918
919    err = compare_file(filename, correct_data, sizeof(correct_data));
920    if (err != 0) {
921        return err;
922    }
923
924    // read
925    fd = open(filename, O_RDONLY);
926    if (fd == -1) {
927        fprintf(stderr, "error opening for read %s\n", filename);
928        return 1;
929    }
930
931    KeyedVector<String8,FileState> readSnapshot;
932    err = read_snapshot_file(fd, &readSnapshot);
933    if (err != 0) {
934        fprintf(stderr, "read_snapshot_file failed %d\n", err);
935        return err;
936    }
937
938    if (readSnapshot.size() != 0) {
939        fprintf(stderr, "readSnapshot should be length 0\n");
940        return 1;
941    }
942
943    return 0;
944}
945
946int
947backup_helper_test_four()
948{
949    int err;
950    int fd;
951    KeyedVector<String8,FileRec> snapshot;
952    const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
953
954    system("rm -r " SCRATCH_DIR);
955    mkdir(SCRATCH_DIR, 0777);
956
957    // write
958    fd = creat(filename, 0666);
959    if (fd == -1) {
960        fprintf(stderr, "error opening %s\n", filename);
961        return 1;
962    }
963
964    String8 filenames[4];
965    FileState states[4];
966    FileRec r;
967    r.deleted = false;
968
969    states[0].modTime_sec = 0xfedcba98;
970    states[0].modTime_nsec = 0xdeadbeef;
971    states[0].mode = 0777; // decimal 511, hex 0x000001ff
972    states[0].size = 0xababbcbc;
973    states[0].crc32 = 0x12345678;
974    states[0].nameLen = -12;
975    r.s = states[0];
976    filenames[0] = String8("bytes_of_padding");
977    snapshot.add(filenames[0], r);
978
979    states[1].modTime_sec = 0x93400031;
980    states[1].modTime_nsec = 0xdeadbeef;
981    states[1].mode = 0666; // decimal 438, hex 0x000001b6
982    states[1].size = 0x88557766;
983    states[1].crc32 = 0x22334422;
984    states[1].nameLen = -1;
985    r.s = states[1];
986    filenames[1] = String8("bytes_of_padding3");
987    snapshot.add(filenames[1], r);
988
989    states[2].modTime_sec = 0x33221144;
990    states[2].modTime_nsec = 0xdeadbeef;
991    states[2].mode = 0744; // decimal 484, hex 0x000001e4
992    states[2].size = 0x11223344;
993    states[2].crc32 = 0x01122334;
994    states[2].nameLen = 0;
995    r.s = states[2];
996    filenames[2] = String8("bytes_of_padding_2");
997    snapshot.add(filenames[2], r);
998
999    states[3].modTime_sec = 0x33221144;
1000    states[3].modTime_nsec = 0xdeadbeef;
1001    states[3].mode = 0755; // decimal 493, hex 0x000001ed
1002    states[3].size = 0x11223344;
1003    states[3].crc32 = 0x01122334;
1004    states[3].nameLen = 0;
1005    r.s = states[3];
1006    filenames[3] = String8("bytes_of_padding__1");
1007    snapshot.add(filenames[3], r);
1008
1009    err = write_snapshot_file(fd, snapshot);
1010
1011    close(fd);
1012
1013    if (err != 0) {
1014        fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
1015        return err;
1016    }
1017
1018    static const unsigned char correct_data[] = {
1019        // header
1020        0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
1021        0x46, 0x69, 0x6c, 0x65,  0xbc, 0x00, 0x00, 0x00,
1022
1023        // bytes_of_padding
1024        0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
1025        0xff, 0x01, 0x00, 0x00,  0xbc, 0xbc, 0xab, 0xab,
1026        0x78, 0x56, 0x34, 0x12,  0x10, 0x00, 0x00, 0x00,
1027        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1028        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1029
1030        // bytes_of_padding3
1031        0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
1032        0xb6, 0x01, 0x00, 0x00,  0x66, 0x77, 0x55, 0x88,
1033        0x22, 0x44, 0x33, 0x22,  0x11, 0x00, 0x00, 0x00,
1034        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1035        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1036        0x33, 0xab, 0xab, 0xab,
1037
1038        // bytes of padding2
1039        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
1040        0xe4, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
1041        0x34, 0x23, 0x12, 0x01,  0x12, 0x00, 0x00, 0x00,
1042        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1043        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1044        0x5f, 0x32, 0xab, 0xab,
1045
1046        // bytes of padding3
1047        0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
1048        0xed, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
1049        0x34, 0x23, 0x12, 0x01,  0x13, 0x00, 0x00, 0x00,
1050        0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
1051        0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
1052        0x5f, 0x5f, 0x31, 0xab
1053    };
1054
1055    err = compare_file(filename, correct_data, sizeof(correct_data));
1056    if (err != 0) {
1057        return err;
1058    }
1059
1060    // read
1061    fd = open(filename, O_RDONLY);
1062    if (fd == -1) {
1063        fprintf(stderr, "error opening for read %s\n", filename);
1064        return 1;
1065    }
1066
1067
1068    KeyedVector<String8,FileState> readSnapshot;
1069    err = read_snapshot_file(fd, &readSnapshot);
1070    if (err != 0) {
1071        fprintf(stderr, "read_snapshot_file failed %d\n", err);
1072        return err;
1073    }
1074
1075    if (readSnapshot.size() != 4) {
1076        fprintf(stderr, "readSnapshot should be length 4 is %zu\n", readSnapshot.size());
1077        return 1;
1078    }
1079
1080    bool matched = true;
1081    for (size_t i=0; i<readSnapshot.size(); i++) {
1082        const String8& name = readSnapshot.keyAt(i);
1083        const FileState state = readSnapshot.valueAt(i);
1084
1085        if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
1086                || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
1087                || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
1088            fprintf(stderr, "state %zu expected={%d/%d, %04o, 0x%08x, 0x%08x, %3zu} '%s'\n"
1089                            "          actual={%d/%d, %04o, 0x%08x, 0x%08x, %3d} '%s'\n", i,
1090                    states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
1091                    states[i].crc32, name.length(), filenames[i].string(),
1092                    state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
1093                    state.nameLen, name.string());
1094            matched = false;
1095        }
1096    }
1097
1098    return matched ? 0 : 1;
1099}
1100
1101// hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
1102const unsigned char DATA_GOLDEN_FILE[] = {
1103     0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
1104     0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
1105     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
1106     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
1107     0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
1108     0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
1109     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1110     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
1111     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1112     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
1113     0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
1114     0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
1115     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
1116     0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
1117     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
1118     0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
1119     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
1120     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
1121     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
1122     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00
1123
1124};
1125const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
1126
1127static int
1128test_write_header_and_entity(BackupDataWriter& writer, const char* str)
1129{
1130    int err;
1131    String8 text(str);
1132
1133    err = writer.WriteEntityHeader(text, text.length()+1);
1134    if (err != 0) {
1135        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
1136        return err;
1137    }
1138
1139    err = writer.WriteEntityData(text.string(), text.length()+1);
1140    if (err != 0) {
1141        fprintf(stderr, "write failed for data '%s'\n", text.string());
1142        return errno;
1143    }
1144
1145    return err;
1146}
1147
1148int
1149backup_helper_test_data_writer()
1150{
1151    int err;
1152    int fd;
1153    const char* filename = SCRATCH_DIR "data_writer.data";
1154
1155    system("rm -r " SCRATCH_DIR);
1156    mkdir(SCRATCH_DIR, 0777);
1157    mkdir(SCRATCH_DIR "data", 0777);
1158
1159    fd = creat(filename, 0666);
1160    if (fd == -1) {
1161        fprintf(stderr, "error creating: %s\n", strerror(errno));
1162        return errno;
1163    }
1164
1165    BackupDataWriter writer(fd);
1166
1167    err = 0;
1168    err |= test_write_header_and_entity(writer, "no_padding_");
1169    err |= test_write_header_and_entity(writer, "padded_to__3");
1170    err |= test_write_header_and_entity(writer, "padded_to_2__");
1171    err |= test_write_header_and_entity(writer, "padded_to1");
1172
1173    close(fd);
1174
1175    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1176    if (err != 0) {
1177        return err;
1178    }
1179
1180    return err;
1181}
1182
1183int
1184test_read_header_and_entity(BackupDataReader& reader, const char* str)
1185{
1186    int err;
1187    size_t bufSize = strlen(str)+1;
1188    char* buf = (char*)malloc(bufSize);
1189    String8 string;
1190    size_t actualSize;
1191    bool done;
1192    int type;
1193    ssize_t nRead;
1194
1195    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
1196
1197    err = reader.ReadNextHeader(&done, &type);
1198    if (done) {
1199        fprintf(stderr, "should not be done yet\n");
1200        goto finished;
1201    }
1202    if (err != 0) {
1203        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
1204        goto finished;
1205    }
1206    if (type != BACKUP_HEADER_ENTITY_V1) {
1207        err = EINVAL;
1208        fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
1209    }
1210
1211    err = reader.ReadEntityHeader(&string, &actualSize);
1212    if (err != 0) {
1213        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
1214        goto finished;
1215    }
1216    if (string != str) {
1217        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
1218        err = EINVAL;
1219        goto finished;
1220    }
1221    if (actualSize != bufSize) {
1222        fprintf(stderr, "ReadEntityHeader expected dataSize %zu got %zu\n",
1223                bufSize, actualSize);
1224        err = EINVAL;
1225        goto finished;
1226    }
1227
1228    nRead = reader.ReadEntityData(buf, bufSize);
1229    if (nRead < 0) {
1230        err = reader.Status();
1231        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
1232        goto finished;
1233    }
1234
1235    if (0 != memcmp(buf, str, bufSize)) {
1236        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
1237                "%02x %02x %02x %02x  '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
1238                buf[0], buf[1], buf[2], buf[3]);
1239        err = EINVAL;
1240        goto finished;
1241    }
1242
1243    // The next read will confirm whether it got the right amount of data.
1244
1245finished:
1246    if (err != NO_ERROR) {
1247        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
1248    }
1249    free(buf);
1250    return err;
1251}
1252
1253int
1254backup_helper_test_data_reader()
1255{
1256    int err;
1257    int fd;
1258    const char* filename = SCRATCH_DIR "data_reader.data";
1259
1260    system("rm -r " SCRATCH_DIR);
1261    mkdir(SCRATCH_DIR, 0777);
1262    mkdir(SCRATCH_DIR "data", 0777);
1263
1264    fd = creat(filename, 0666);
1265    if (fd == -1) {
1266        fprintf(stderr, "error creating: %s\n", strerror(errno));
1267        return errno;
1268    }
1269
1270    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1271    if (err != DATA_GOLDEN_FILE_SIZE) {
1272        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
1273        return errno;
1274    }
1275
1276    close(fd);
1277
1278    fd = open(filename, O_RDONLY);
1279    if (fd == -1) {
1280        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
1281                filename);
1282        return errno;
1283    }
1284
1285    {
1286        BackupDataReader reader(fd);
1287
1288        err = 0;
1289
1290        if (err == NO_ERROR) {
1291            err = test_read_header_and_entity(reader, "no_padding_");
1292        }
1293
1294        if (err == NO_ERROR) {
1295            err = test_read_header_and_entity(reader, "padded_to__3");
1296        }
1297
1298        if (err == NO_ERROR) {
1299            err = test_read_header_and_entity(reader, "padded_to_2__");
1300        }
1301
1302        if (err == NO_ERROR) {
1303            err = test_read_header_and_entity(reader, "padded_to1");
1304        }
1305    }
1306
1307    close(fd);
1308
1309    return err;
1310}
1311
1312static int
1313get_mod_time(const char* filename, struct timeval times[2])
1314{
1315    int err;
1316    struct stat64 st;
1317    err = stat64(filename, &st);
1318    if (err != 0) {
1319        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
1320        return errno;
1321    }
1322
1323    times[0].tv_sec = st.st_atim.tv_sec;
1324    times[0].tv_usec = st.st_atim.tv_nsec / 1000;
1325
1326    times[1].tv_sec = st.st_mtim.tv_sec;
1327    times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1328
1329    return 0;
1330}
1331
1332int
1333backup_helper_test_files()
1334{
1335    int err;
1336    int oldSnapshotFD;
1337    int dataStreamFD;
1338    int newSnapshotFD;
1339
1340    system("rm -r " SCRATCH_DIR);
1341    mkdir(SCRATCH_DIR, 0777);
1342    mkdir(SCRATCH_DIR "data", 0777);
1343
1344    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1345    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1346    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
1347    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
1348    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
1349    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
1350
1351    char const* files_before[] = {
1352        SCRATCH_DIR "data/b",
1353        SCRATCH_DIR "data/c",
1354        SCRATCH_DIR "data/d",
1355        SCRATCH_DIR "data/e",
1356        SCRATCH_DIR "data/f"
1357    };
1358
1359    char const* keys_before[] = {
1360        "data/b",
1361        "data/c",
1362        "data/d",
1363        "data/e",
1364        "data/f"
1365    };
1366
1367    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
1368    if (dataStreamFD == -1) {
1369        fprintf(stderr, "error creating: %s\n", strerror(errno));
1370        return errno;
1371    }
1372
1373    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
1374    if (newSnapshotFD == -1) {
1375        fprintf(stderr, "error creating: %s\n", strerror(errno));
1376        return errno;
1377    }
1378
1379    {
1380        BackupDataWriter dataStream(dataStreamFD);
1381
1382        err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
1383        if (err != 0) {
1384            return err;
1385        }
1386    }
1387
1388    close(dataStreamFD);
1389    close(newSnapshotFD);
1390
1391    sleep(3);
1392
1393    struct timeval d_times[2];
1394    struct timeval e_times[2];
1395
1396    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
1397    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
1398    if (err != 0) {
1399        return err;
1400    }
1401
1402    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1403    unlink(SCRATCH_DIR "data/c");
1404    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1405    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
1406    utimes(SCRATCH_DIR "data/d", d_times);
1407    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
1408    utimes(SCRATCH_DIR "data/e", e_times);
1409    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
1410    unlink(SCRATCH_DIR "data/f");
1411
1412    char const* files_after[] = {
1413        SCRATCH_DIR "data/a", // added
1414        SCRATCH_DIR "data/b", // same
1415        SCRATCH_DIR "data/c", // different mod time
1416        SCRATCH_DIR "data/d", // different size (same mod time)
1417        SCRATCH_DIR "data/e", // different contents (same mod time, same size)
1418        SCRATCH_DIR "data/g"  // added
1419    };
1420
1421    char const* keys_after[] = {
1422        "data/a", // added
1423        "data/b", // same
1424        "data/c", // different mod time
1425        "data/d", // different size (same mod time)
1426        "data/e", // different contents (same mod time, same size)
1427        "data/g"  // added
1428    };
1429
1430    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
1431    if (oldSnapshotFD == -1) {
1432        fprintf(stderr, "error opening: %s\n", strerror(errno));
1433        return errno;
1434    }
1435
1436    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
1437    if (dataStreamFD == -1) {
1438        fprintf(stderr, "error creating: %s\n", strerror(errno));
1439        return errno;
1440    }
1441
1442    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
1443    if (newSnapshotFD == -1) {
1444        fprintf(stderr, "error creating: %s\n", strerror(errno));
1445        return errno;
1446    }
1447
1448    {
1449        BackupDataWriter dataStream(dataStreamFD);
1450
1451        err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
1452        if (err != 0) {
1453            return err;
1454        }
1455}
1456
1457    close(oldSnapshotFD);
1458    close(dataStreamFD);
1459    close(newSnapshotFD);
1460
1461    return 0;
1462}
1463
1464int
1465backup_helper_test_null_base()
1466{
1467    int err;
1468    int dataStreamFD;
1469    int newSnapshotFD;
1470
1471    system("rm -r " SCRATCH_DIR);
1472    mkdir(SCRATCH_DIR, 0777);
1473    mkdir(SCRATCH_DIR "data", 0777);
1474
1475    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1476
1477    char const* files[] = {
1478        SCRATCH_DIR "data/a",
1479    };
1480
1481    char const* keys[] = {
1482        "a",
1483    };
1484
1485    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1486    if (dataStreamFD == -1) {
1487        fprintf(stderr, "error creating: %s\n", strerror(errno));
1488        return errno;
1489    }
1490
1491    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1492    if (newSnapshotFD == -1) {
1493        fprintf(stderr, "error creating: %s\n", strerror(errno));
1494        return errno;
1495    }
1496
1497    {
1498        BackupDataWriter dataStream(dataStreamFD);
1499
1500        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1501        if (err != 0) {
1502            return err;
1503        }
1504    }
1505
1506    close(dataStreamFD);
1507    close(newSnapshotFD);
1508
1509    return 0;
1510}
1511
1512int
1513backup_helper_test_missing_file()
1514{
1515    int err;
1516    int dataStreamFD;
1517    int newSnapshotFD;
1518
1519    system("rm -r " SCRATCH_DIR);
1520    mkdir(SCRATCH_DIR, 0777);
1521    mkdir(SCRATCH_DIR "data", 0777);
1522
1523    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1524
1525    char const* files[] = {
1526        SCRATCH_DIR "data/a",
1527        SCRATCH_DIR "data/b",
1528        SCRATCH_DIR "data/c",
1529    };
1530
1531    char const* keys[] = {
1532        "a",
1533        "b",
1534        "c",
1535    };
1536
1537    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1538    if (dataStreamFD == -1) {
1539        fprintf(stderr, "error creating: %s\n", strerror(errno));
1540        return errno;
1541    }
1542
1543    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1544    if (newSnapshotFD == -1) {
1545        fprintf(stderr, "error creating: %s\n", strerror(errno));
1546        return errno;
1547    }
1548
1549    {
1550        BackupDataWriter dataStream(dataStreamFD);
1551
1552        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1553        if (err != 0) {
1554            return err;
1555        }
1556    }
1557
1558    close(dataStreamFD);
1559    close(newSnapshotFD);
1560
1561    return 0;
1562}
1563
1564
1565#endif // TEST_BACKUP_HELPERS
1566
1567}
1568