Home | History | Annotate | Download | only in utils
      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 
     39 namespace 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 
     62 struct file_metadata_v1 {
     63     int version;
     64     int mode;
     65     int undefined_1;
     66     int undefined_2;
     67 };
     68 
     69 const 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...) LOGD(x)
     78 #endif
     79 #endif
     80 
     81 const static int ROUND_UP[4] = { 0, 3, 2, 1 };
     82 
     83 static inline int
     84 round_up(int n)
     85 {
     86     return n + ROUND_UP[n % 4];
     87 }
     88 
     89 static int
     90 read_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         LOGW("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             LOGW("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             LOGW("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         LOGW("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 
    146 static int
    147 write_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         LOGW("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                 LOGW("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                 LOGW("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                     LOGW("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 
    207 static int
    208 write_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 
    214 static int
    215 write_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         LOGE("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         LOGE("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 
    291 static int
    292 write_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 
    312 static int
    313 compute_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 
    331 int
    332 back_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 #define RESTORE_BUF_SIZE (8*1024)
    446 
    447 RestoreHelperBase::RestoreHelperBase()
    448 {
    449     m_buf = malloc(RESTORE_BUF_SIZE);
    450     m_loggedUnknownMetadata = false;
    451 }
    452 
    453 RestoreHelperBase::~RestoreHelperBase()
    454 {
    455     free(m_buf);
    456 }
    457 
    458 status_t
    459 RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
    460 {
    461     ssize_t err;
    462     size_t dataSize;
    463     String8 key;
    464     int fd;
    465     void* buf = m_buf;
    466     ssize_t amt;
    467     int mode;
    468     int crc;
    469     struct stat st;
    470     FileRec r;
    471 
    472     err = in->ReadEntityHeader(&key, &dataSize);
    473     if (err != NO_ERROR) {
    474         return err;
    475     }
    476 
    477     // Get the metadata block off the head of the file entity and use that to
    478     // set up the output file
    479     file_metadata_v1 metadata;
    480     amt = in->ReadEntityData(&metadata, sizeof(metadata));
    481     if (amt != sizeof(metadata)) {
    482         LOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
    483                 (long)amt, strerror(errno));
    484         return EIO;
    485     }
    486     metadata.version = fromlel(metadata.version);
    487     metadata.mode = fromlel(metadata.mode);
    488     if (metadata.version > CURRENT_METADATA_VERSION) {
    489         if (!m_loggedUnknownMetadata) {
    490             m_loggedUnknownMetadata = true;
    491             LOGW("Restoring file with unsupported metadata version %d (currently %d)",
    492                     metadata.version, CURRENT_METADATA_VERSION);
    493         }
    494     }
    495     mode = metadata.mode;
    496 
    497     // Write the file and compute the crc
    498     crc = crc32(0L, Z_NULL, 0);
    499     fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
    500     if (fd == -1) {
    501         LOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
    502         return errno;
    503     }
    504 
    505     while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
    506         err = write(fd, buf, amt);
    507         if (err != amt) {
    508             close(fd);
    509             LOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
    510             return errno;
    511         }
    512         crc = crc32(crc, (Bytef*)buf, amt);
    513     }
    514 
    515     close(fd);
    516 
    517     // Record for the snapshot
    518     err = stat(filename.string(), &st);
    519     if (err != 0) {
    520         LOGW("Error stating file that we just created %s", filename.string());
    521         return errno;
    522     }
    523 
    524     r.file = filename;
    525     r.deleted = false;
    526     r.s.modTime_sec = st.st_mtime;
    527     r.s.modTime_nsec = 0; // workaround sim breakage
    528     //r.s.modTime_nsec = st.st_mtime_nsec;
    529     r.s.mode = st.st_mode;
    530     r.s.size = st.st_size;
    531     r.s.crc32 = crc;
    532 
    533     m_files.add(key, r);
    534 
    535     return NO_ERROR;
    536 }
    537 
    538 status_t
    539 RestoreHelperBase::WriteSnapshot(int fd)
    540 {
    541     return write_snapshot_file(fd, m_files);;
    542 }
    543 
    544 #if TEST_BACKUP_HELPERS
    545 
    546 #define SCRATCH_DIR "/data/backup_helper_test/"
    547 
    548 static int
    549 write_text_file(const char* path, const char* data)
    550 {
    551     int amt;
    552     int fd;
    553     int len;
    554 
    555     fd = creat(path, 0666);
    556     if (fd == -1) {
    557         fprintf(stderr, "creat %s failed\n", path);
    558         return errno;
    559     }
    560 
    561     len = strlen(data);
    562     amt = write(fd, data, len);
    563     if (amt != len) {
    564         fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path);
    565         return errno;
    566     }
    567 
    568     close(fd);
    569 
    570     return 0;
    571 }
    572 
    573 static int
    574 compare_file(const char* path, const unsigned char* data, int len)
    575 {
    576     int fd;
    577     int amt;
    578 
    579     fd = open(path, O_RDONLY);
    580     if (fd == -1) {
    581         fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path);
    582         return errno;
    583     }
    584 
    585     unsigned char* contents = (unsigned char*)malloc(len);
    586     if (contents == NULL) {
    587         fprintf(stderr, "malloc(%d) failed\n", len);
    588         return ENOMEM;
    589     }
    590 
    591     bool sizesMatch = true;
    592     amt = lseek(fd, 0, SEEK_END);
    593     if (amt != len) {
    594         fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt);
    595         sizesMatch = false;
    596     }
    597     lseek(fd, 0, SEEK_SET);
    598 
    599     int readLen = amt < len ? amt : len;
    600     amt = read(fd, contents, readLen);
    601     if (amt != readLen) {
    602         fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt);
    603     }
    604 
    605     bool contentsMatch = true;
    606     for (int i=0; i<readLen; i++) {
    607         if (data[i] != contents[i]) {
    608             if (contentsMatch) {
    609                 fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n");
    610                 contentsMatch = false;
    611             }
    612             fprintf(stderr, "  [%-2d] %02x %02x\n", i, data[i], contents[i]);
    613         }
    614     }
    615 
    616     free(contents);
    617     return contentsMatch && sizesMatch ? 0 : 1;
    618 }
    619 
    620 int
    621 backup_helper_test_empty()
    622 {
    623     int err;
    624     int fd;
    625     KeyedVector<String8,FileRec> snapshot;
    626     const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap";
    627 
    628     system("rm -r " SCRATCH_DIR);
    629     mkdir(SCRATCH_DIR, 0777);
    630 
    631     // write
    632     fd = creat(filename, 0666);
    633     if (fd == -1) {
    634         fprintf(stderr, "error creating %s\n", filename);
    635         return 1;
    636     }
    637 
    638     err = write_snapshot_file(fd, snapshot);
    639 
    640     close(fd);
    641 
    642     if (err != 0) {
    643         fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
    644         return err;
    645     }
    646 
    647     static const unsigned char correct_data[] = {
    648         0x53, 0x6e, 0x61, 0x70,  0x00, 0x00, 0x00, 0x00,
    649         0x46, 0x69, 0x6c, 0x65,  0x10, 0x00, 0x00, 0x00
    650     };
    651 
    652     err = compare_file(filename, correct_data, sizeof(correct_data));
    653     if (err != 0) {
    654         return err;
    655     }
    656 
    657     // read
    658     fd = open(filename, O_RDONLY);
    659     if (fd == -1) {
    660         fprintf(stderr, "error opening for read %s\n", filename);
    661         return 1;
    662     }
    663 
    664     KeyedVector<String8,FileState> readSnapshot;
    665     err = read_snapshot_file(fd, &readSnapshot);
    666     if (err != 0) {
    667         fprintf(stderr, "read_snapshot_file failed %d\n", err);
    668         return err;
    669     }
    670 
    671     if (readSnapshot.size() != 0) {
    672         fprintf(stderr, "readSnapshot should be length 0\n");
    673         return 1;
    674     }
    675 
    676     return 0;
    677 }
    678 
    679 int
    680 backup_helper_test_four()
    681 {
    682     int err;
    683     int fd;
    684     KeyedVector<String8,FileRec> snapshot;
    685     const char* filename = SCRATCH_DIR "backup_helper_test_four.snap";
    686 
    687     system("rm -r " SCRATCH_DIR);
    688     mkdir(SCRATCH_DIR, 0777);
    689 
    690     // write
    691     fd = creat(filename, 0666);
    692     if (fd == -1) {
    693         fprintf(stderr, "error opening %s\n", filename);
    694         return 1;
    695     }
    696 
    697     String8 filenames[4];
    698     FileState states[4];
    699     FileRec r;
    700     r.deleted = false;
    701 
    702     states[0].modTime_sec = 0xfedcba98;
    703     states[0].modTime_nsec = 0xdeadbeef;
    704     states[0].mode = 0777; // decimal 511, hex 0x000001ff
    705     states[0].size = 0xababbcbc;
    706     states[0].crc32 = 0x12345678;
    707     states[0].nameLen = -12;
    708     r.s = states[0];
    709     filenames[0] = String8("bytes_of_padding");
    710     snapshot.add(filenames[0], r);
    711 
    712     states[1].modTime_sec = 0x93400031;
    713     states[1].modTime_nsec = 0xdeadbeef;
    714     states[1].mode = 0666; // decimal 438, hex 0x000001b6
    715     states[1].size = 0x88557766;
    716     states[1].crc32 = 0x22334422;
    717     states[1].nameLen = -1;
    718     r.s = states[1];
    719     filenames[1] = String8("bytes_of_padding3");
    720     snapshot.add(filenames[1], r);
    721 
    722     states[2].modTime_sec = 0x33221144;
    723     states[2].modTime_nsec = 0xdeadbeef;
    724     states[2].mode = 0744; // decimal 484, hex 0x000001e4
    725     states[2].size = 0x11223344;
    726     states[2].crc32 = 0x01122334;
    727     states[2].nameLen = 0;
    728     r.s = states[2];
    729     filenames[2] = String8("bytes_of_padding_2");
    730     snapshot.add(filenames[2], r);
    731 
    732     states[3].modTime_sec = 0x33221144;
    733     states[3].modTime_nsec = 0xdeadbeef;
    734     states[3].mode = 0755; // decimal 493, hex 0x000001ed
    735     states[3].size = 0x11223344;
    736     states[3].crc32 = 0x01122334;
    737     states[3].nameLen = 0;
    738     r.s = states[3];
    739     filenames[3] = String8("bytes_of_padding__1");
    740     snapshot.add(filenames[3], r);
    741 
    742     err = write_snapshot_file(fd, snapshot);
    743 
    744     close(fd);
    745 
    746     if (err != 0) {
    747         fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err));
    748         return err;
    749     }
    750 
    751     static const unsigned char correct_data[] = {
    752         // header
    753         0x53, 0x6e, 0x61, 0x70,  0x04, 0x00, 0x00, 0x00,
    754         0x46, 0x69, 0x6c, 0x65,  0xbc, 0x00, 0x00, 0x00,
    755 
    756         // bytes_of_padding
    757         0x98, 0xba, 0xdc, 0xfe,  0xef, 0xbe, 0xad, 0xde,
    758         0xff, 0x01, 0x00, 0x00,  0xbc, 0xbc, 0xab, 0xab,
    759         0x78, 0x56, 0x34, 0x12,  0x10, 0x00, 0x00, 0x00,
    760         0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
    761         0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
    762 
    763         // bytes_of_padding3
    764         0x31, 0x00, 0x40, 0x93,  0xef, 0xbe, 0xad, 0xde,
    765         0xb6, 0x01, 0x00, 0x00,  0x66, 0x77, 0x55, 0x88,
    766         0x22, 0x44, 0x33, 0x22,  0x11, 0x00, 0x00, 0x00,
    767         0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
    768         0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
    769         0x33, 0xab, 0xab, 0xab,
    770 
    771         // bytes of padding2
    772         0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
    773         0xe4, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
    774         0x34, 0x23, 0x12, 0x01,  0x12, 0x00, 0x00, 0x00,
    775         0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
    776         0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
    777         0x5f, 0x32, 0xab, 0xab,
    778 
    779         // bytes of padding3
    780         0x44, 0x11, 0x22, 0x33,  0xef, 0xbe, 0xad, 0xde,
    781         0xed, 0x01, 0x00, 0x00,  0x44, 0x33, 0x22, 0x11,
    782         0x34, 0x23, 0x12, 0x01,  0x13, 0x00, 0x00, 0x00,
    783         0x62, 0x79, 0x74, 0x65,  0x73, 0x5f, 0x6f, 0x66,
    784         0x5f, 0x70, 0x61, 0x64,  0x64, 0x69, 0x6e, 0x67,
    785         0x5f, 0x5f, 0x31, 0xab
    786     };
    787 
    788     err = compare_file(filename, correct_data, sizeof(correct_data));
    789     if (err != 0) {
    790         return err;
    791     }
    792 
    793     // read
    794     fd = open(filename, O_RDONLY);
    795     if (fd == -1) {
    796         fprintf(stderr, "error opening for read %s\n", filename);
    797         return 1;
    798     }
    799 
    800 
    801     KeyedVector<String8,FileState> readSnapshot;
    802     err = read_snapshot_file(fd, &readSnapshot);
    803     if (err != 0) {
    804         fprintf(stderr, "read_snapshot_file failed %d\n", err);
    805         return err;
    806     }
    807 
    808     if (readSnapshot.size() != 4) {
    809         fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size());
    810         return 1;
    811     }
    812 
    813     bool matched = true;
    814     for (size_t i=0; i<readSnapshot.size(); i++) {
    815         const String8& name = readSnapshot.keyAt(i);
    816         const FileState state = readSnapshot.valueAt(i);
    817 
    818         if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
    819                 || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
    820                 || states[i].size != state.size || states[i].crc32 != states[i].crc32) {
    821             fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n"
    822                             "          actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i,
    823                     states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
    824                     states[i].crc32, name.length(), filenames[i].string(),
    825                     state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
    826                     state.nameLen, name.string());
    827             matched = false;
    828         }
    829     }
    830 
    831     return matched ? 0 : 1;
    832 }
    833 
    834 // hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
    835 const unsigned char DATA_GOLDEN_FILE[] = {
    836      0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
    837      0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
    838      0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
    839      0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
    840      0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61,
    841      0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
    842      0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
    843      0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
    844      0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
    845      0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
    846      0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
    847      0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
    848      0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
    849      0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
    850      0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
    851      0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61,
    852      0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
    853      0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
    854      0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
    855      0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00
    856 
    857 };
    858 const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
    859 
    860 static int
    861 test_write_header_and_entity(BackupDataWriter& writer, const char* str)
    862 {
    863     int err;
    864     String8 text(str);
    865 
    866     err = writer.WriteEntityHeader(text, text.length()+1);
    867     if (err != 0) {
    868         fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
    869         return err;
    870     }
    871 
    872     err = writer.WriteEntityData(text.string(), text.length()+1);
    873     if (err != 0) {
    874         fprintf(stderr, "write failed for data '%s'\n", text.string());
    875         return errno;
    876     }
    877 
    878     return err;
    879 }
    880 
    881 int
    882 backup_helper_test_data_writer()
    883 {
    884     int err;
    885     int fd;
    886     const char* filename = SCRATCH_DIR "data_writer.data";
    887 
    888     system("rm -r " SCRATCH_DIR);
    889     mkdir(SCRATCH_DIR, 0777);
    890     mkdir(SCRATCH_DIR "data", 0777);
    891 
    892     fd = creat(filename, 0666);
    893     if (fd == -1) {
    894         fprintf(stderr, "error creating: %s\n", strerror(errno));
    895         return errno;
    896     }
    897 
    898     BackupDataWriter writer(fd);
    899 
    900     err = 0;
    901     err |= test_write_header_and_entity(writer, "no_padding_");
    902     err |= test_write_header_and_entity(writer, "padded_to__3");
    903     err |= test_write_header_and_entity(writer, "padded_to_2__");
    904     err |= test_write_header_and_entity(writer, "padded_to1");
    905 
    906     close(fd);
    907 
    908     err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
    909     if (err != 0) {
    910         return err;
    911     }
    912 
    913     return err;
    914 }
    915 
    916 int
    917 test_read_header_and_entity(BackupDataReader& reader, const char* str)
    918 {
    919     int err;
    920     int bufSize = strlen(str)+1;
    921     char* buf = (char*)malloc(bufSize);
    922     String8 string;
    923     int cookie = 0x11111111;
    924     size_t actualSize;
    925     bool done;
    926     int type;
    927     ssize_t nRead;
    928 
    929     // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
    930 
    931     err = reader.ReadNextHeader(&done, &type);
    932     if (done) {
    933         fprintf(stderr, "should not be done yet\n");
    934         goto finished;
    935     }
    936     if (err != 0) {
    937         fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
    938         goto finished;
    939     }
    940     if (type != BACKUP_HEADER_ENTITY_V1) {
    941         err = EINVAL;
    942         fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
    943     }
    944 
    945     err = reader.ReadEntityHeader(&string, &actualSize);
    946     if (err != 0) {
    947         fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
    948         goto finished;
    949     }
    950     if (string != str) {
    951         fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
    952         err = EINVAL;
    953         goto finished;
    954     }
    955     if ((int)actualSize != bufSize) {
    956         fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
    957                 actualSize);
    958         err = EINVAL;
    959         goto finished;
    960     }
    961 
    962     nRead = reader.ReadEntityData(buf, bufSize);
    963     if (nRead < 0) {
    964         err = reader.Status();
    965         fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
    966         goto finished;
    967     }
    968 
    969     if (0 != memcmp(buf, str, bufSize)) {
    970         fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
    971                 "%02x %02x %02x %02x  '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
    972                 buf[0], buf[1], buf[2], buf[3]);
    973         err = EINVAL;
    974         goto finished;
    975     }
    976 
    977     // The next read will confirm whether it got the right amount of data.
    978 
    979 finished:
    980     if (err != NO_ERROR) {
    981         fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
    982     }
    983     free(buf);
    984     return err;
    985 }
    986 
    987 int
    988 backup_helper_test_data_reader()
    989 {
    990     int err;
    991     int fd;
    992     const char* filename = SCRATCH_DIR "data_reader.data";
    993 
    994     system("rm -r " SCRATCH_DIR);
    995     mkdir(SCRATCH_DIR, 0777);
    996     mkdir(SCRATCH_DIR "data", 0777);
    997 
    998     fd = creat(filename, 0666);
    999     if (fd == -1) {
   1000         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1001         return errno;
   1002     }
   1003 
   1004     err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
   1005     if (err != DATA_GOLDEN_FILE_SIZE) {
   1006         fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
   1007         return errno;
   1008     }
   1009 
   1010     close(fd);
   1011 
   1012     fd = open(filename, O_RDONLY);
   1013     if (fd == -1) {
   1014         fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
   1015                 filename);
   1016         return errno;
   1017     }
   1018 
   1019     {
   1020         BackupDataReader reader(fd);
   1021 
   1022         err = 0;
   1023 
   1024         if (err == NO_ERROR) {
   1025             err = test_read_header_and_entity(reader, "no_padding_");
   1026         }
   1027 
   1028         if (err == NO_ERROR) {
   1029             err = test_read_header_and_entity(reader, "padded_to__3");
   1030         }
   1031 
   1032         if (err == NO_ERROR) {
   1033             err = test_read_header_and_entity(reader, "padded_to_2__");
   1034         }
   1035 
   1036         if (err == NO_ERROR) {
   1037             err = test_read_header_and_entity(reader, "padded_to1");
   1038         }
   1039     }
   1040 
   1041     close(fd);
   1042 
   1043     return err;
   1044 }
   1045 
   1046 static int
   1047 get_mod_time(const char* filename, struct timeval times[2])
   1048 {
   1049     int err;
   1050     struct stat64 st;
   1051     err = stat64(filename, &st);
   1052     if (err != 0) {
   1053         fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
   1054         return errno;
   1055     }
   1056     times[0].tv_sec = st.st_atime;
   1057     times[1].tv_sec = st.st_mtime;
   1058 
   1059     // If st_atime is a macro then struct stat64 uses struct timespec
   1060     // to store the access and modif time values and typically
   1061     // st_*time_nsec is not defined. In glibc, this is controlled by
   1062     // __USE_MISC.
   1063 #ifdef __USE_MISC
   1064 #if !defined(st_atime) || defined(st_atime_nsec)
   1065 #error "Check if this __USE_MISC conditional is still needed."
   1066 #endif
   1067     times[0].tv_usec = st.st_atim.tv_nsec / 1000;
   1068     times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
   1069 #else
   1070     times[0].tv_usec = st.st_atime_nsec / 1000;
   1071     times[1].tv_usec = st.st_mtime_nsec / 1000;
   1072 #endif
   1073 
   1074     return 0;
   1075 }
   1076 
   1077 int
   1078 backup_helper_test_files()
   1079 {
   1080     int err;
   1081     int oldSnapshotFD;
   1082     int dataStreamFD;
   1083     int newSnapshotFD;
   1084 
   1085     system("rm -r " SCRATCH_DIR);
   1086     mkdir(SCRATCH_DIR, 0777);
   1087     mkdir(SCRATCH_DIR "data", 0777);
   1088 
   1089     write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
   1090     write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
   1091     write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
   1092     write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
   1093     write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
   1094     write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
   1095 
   1096     char const* files_before[] = {
   1097         SCRATCH_DIR "data/b",
   1098         SCRATCH_DIR "data/c",
   1099         SCRATCH_DIR "data/d",
   1100         SCRATCH_DIR "data/e",
   1101         SCRATCH_DIR "data/f"
   1102     };
   1103 
   1104     char const* keys_before[] = {
   1105         "data/b",
   1106         "data/c",
   1107         "data/d",
   1108         "data/e",
   1109         "data/f"
   1110     };
   1111 
   1112     dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
   1113     if (dataStreamFD == -1) {
   1114         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1115         return errno;
   1116     }
   1117 
   1118     newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
   1119     if (newSnapshotFD == -1) {
   1120         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1121         return errno;
   1122     }
   1123 
   1124     {
   1125         BackupDataWriter dataStream(dataStreamFD);
   1126 
   1127         err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
   1128         if (err != 0) {
   1129             return err;
   1130         }
   1131     }
   1132 
   1133     close(dataStreamFD);
   1134     close(newSnapshotFD);
   1135 
   1136     sleep(3);
   1137 
   1138     struct timeval d_times[2];
   1139     struct timeval e_times[2];
   1140 
   1141     err = get_mod_time(SCRATCH_DIR "data/d", d_times);
   1142     err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
   1143     if (err != 0) {
   1144         return err;
   1145     }
   1146 
   1147     write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
   1148     unlink(SCRATCH_DIR "data/c");
   1149     write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
   1150     write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
   1151     utimes(SCRATCH_DIR "data/d", d_times);
   1152     write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
   1153     utimes(SCRATCH_DIR "data/e", e_times);
   1154     write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
   1155     unlink(SCRATCH_DIR "data/f");
   1156 
   1157     char const* files_after[] = {
   1158         SCRATCH_DIR "data/a", // added
   1159         SCRATCH_DIR "data/b", // same
   1160         SCRATCH_DIR "data/c", // different mod time
   1161         SCRATCH_DIR "data/d", // different size (same mod time)
   1162         SCRATCH_DIR "data/e", // different contents (same mod time, same size)
   1163         SCRATCH_DIR "data/g"  // added
   1164     };
   1165 
   1166     char const* keys_after[] = {
   1167         "data/a", // added
   1168         "data/b", // same
   1169         "data/c", // different mod time
   1170         "data/d", // different size (same mod time)
   1171         "data/e", // different contents (same mod time, same size)
   1172         "data/g"  // added
   1173     };
   1174 
   1175     oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
   1176     if (oldSnapshotFD == -1) {
   1177         fprintf(stderr, "error opening: %s\n", strerror(errno));
   1178         return errno;
   1179     }
   1180 
   1181     dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
   1182     if (dataStreamFD == -1) {
   1183         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1184         return errno;
   1185     }
   1186 
   1187     newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
   1188     if (newSnapshotFD == -1) {
   1189         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1190         return errno;
   1191     }
   1192 
   1193     {
   1194         BackupDataWriter dataStream(dataStreamFD);
   1195 
   1196         err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
   1197         if (err != 0) {
   1198             return err;
   1199         }
   1200 }
   1201 
   1202     close(oldSnapshotFD);
   1203     close(dataStreamFD);
   1204     close(newSnapshotFD);
   1205 
   1206     return 0;
   1207 }
   1208 
   1209 int
   1210 backup_helper_test_null_base()
   1211 {
   1212     int err;
   1213     int oldSnapshotFD;
   1214     int dataStreamFD;
   1215     int newSnapshotFD;
   1216 
   1217     system("rm -r " SCRATCH_DIR);
   1218     mkdir(SCRATCH_DIR, 0777);
   1219     mkdir(SCRATCH_DIR "data", 0777);
   1220 
   1221     write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
   1222 
   1223     char const* files[] = {
   1224         SCRATCH_DIR "data/a",
   1225     };
   1226 
   1227     char const* keys[] = {
   1228         "a",
   1229     };
   1230 
   1231     dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
   1232     if (dataStreamFD == -1) {
   1233         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1234         return errno;
   1235     }
   1236 
   1237     newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
   1238     if (newSnapshotFD == -1) {
   1239         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1240         return errno;
   1241     }
   1242 
   1243     {
   1244         BackupDataWriter dataStream(dataStreamFD);
   1245 
   1246         err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
   1247         if (err != 0) {
   1248             return err;
   1249         }
   1250     }
   1251 
   1252     close(dataStreamFD);
   1253     close(newSnapshotFD);
   1254 
   1255     return 0;
   1256 }
   1257 
   1258 int
   1259 backup_helper_test_missing_file()
   1260 {
   1261     int err;
   1262     int oldSnapshotFD;
   1263     int dataStreamFD;
   1264     int newSnapshotFD;
   1265 
   1266     system("rm -r " SCRATCH_DIR);
   1267     mkdir(SCRATCH_DIR, 0777);
   1268     mkdir(SCRATCH_DIR "data", 0777);
   1269 
   1270     write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
   1271 
   1272     char const* files[] = {
   1273         SCRATCH_DIR "data/a",
   1274         SCRATCH_DIR "data/b",
   1275         SCRATCH_DIR "data/c",
   1276     };
   1277 
   1278     char const* keys[] = {
   1279         "a",
   1280         "b",
   1281         "c",
   1282     };
   1283 
   1284     dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
   1285     if (dataStreamFD == -1) {
   1286         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1287         return errno;
   1288     }
   1289 
   1290     newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
   1291     if (newSnapshotFD == -1) {
   1292         fprintf(stderr, "error creating: %s\n", strerror(errno));
   1293         return errno;
   1294     }
   1295 
   1296     {
   1297         BackupDataWriter dataStream(dataStreamFD);
   1298 
   1299         err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
   1300         if (err != 0) {
   1301             return err;
   1302         }
   1303     }
   1304 
   1305     close(dataStreamFD);
   1306     close(newSnapshotFD);
   1307 
   1308     return 0;
   1309 }
   1310 
   1311 
   1312 #endif // TEST_BACKUP_HELPERS
   1313 
   1314 }
   1315