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