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