BackupHelpers.cpp revision 52eb4e01a49fe2e94555c000de38bbcbbb13401b
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 <errno.h>
22#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <sys/time.h>  // for utimes
28#include <sys/uio.h>
29#include <unistd.h>
30#include <utime.h>
31#include <zlib.h>
32
33#include <log/log.h>
34#include <utils/ByteOrder.h>
35#include <utils/KeyedVector.h>
36#include <utils/String8.h>
37
38namespace android {
39
40#define MAGIC0 0x70616e53 // Snap
41#define MAGIC1 0x656c6946 // File
42
43/*
44 * File entity data format (v1):
45 *
46 *   - 4-byte version number of the metadata, little endian (0x00000001 for v1)
47 *   - 12 bytes of metadata
48 *   - the file data itself
49 *
50 * i.e. a 16-byte metadata header followed by the raw file data.  If the
51 * restore code does not recognize the metadata version, it can still
52 * interpret the file data itself correctly.
53 *
54 * file_metadata_v1:
55 *
56 *   - 4 byte version number === 0x00000001 (little endian)
57 *   - 4-byte access mode (little-endian)
58 *   - undefined (8 bytes)
59 */
60
61struct file_metadata_v1 {
62    int version;
63    int mode;
64    int undefined_1;
65    int undefined_2;
66};
67
68const static int CURRENT_METADATA_VERSION = 1;
69
70static const bool kIsDebug = false;
71#if TEST_BACKUP_HELPERS
72#define LOGP(f, x...) if (kIsDebug) printf(f "\n", x)
73#else
74#define LOGP(x...) if (kIsDebug) ALOGD(x)
75#endif
76
77const static int ROUND_UP[4] = { 0, 3, 2, 1 };
78
79static inline int
80round_up(int n)
81{
82    return n + ROUND_UP[n % 4];
83}
84
85static int
86read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot)
87{
88    int bytesRead = 0;
89    int amt;
90    SnapshotHeader header;
91
92    amt = read(fd, &header, sizeof(header));
93    if (amt != sizeof(header)) {
94        return errno;
95    }
96    bytesRead += amt;
97
98    if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) {
99        ALOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1);
100        return 1;
101    }
102
103    for (int i=0; i<header.fileCount; i++) {
104        FileState file;
105        char filenameBuf[128];
106
107        amt = read(fd, &file, sizeof(FileState));
108        if (amt != sizeof(FileState)) {
109            ALOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead);
110            return 1;
111        }
112        bytesRead += amt;
113
114        // filename is not NULL terminated, but it is padded
115        int nameBufSize = round_up(file.nameLen);
116        char* filename = nameBufSize <= (int)sizeof(filenameBuf)
117                ? filenameBuf
118                : (char*)malloc(nameBufSize);
119        amt = read(fd, filename, nameBufSize);
120        if (amt == nameBufSize) {
121            snapshot->add(String8(filename, file.nameLen), file);
122        }
123        bytesRead += amt;
124        if (filename != filenameBuf) {
125            free(filename);
126        }
127        if (amt != nameBufSize) {
128            ALOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead);
129            return 1;
130        }
131    }
132
133    if (header.totalSize != bytesRead) {
134        ALOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n",
135                header.totalSize, bytesRead);
136        return 1;
137    }
138
139    return 0;
140}
141
142static int
143write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot)
144{
145    int fileCount = 0;
146    int bytesWritten = sizeof(SnapshotHeader);
147    // preflight size
148    const int N = snapshot.size();
149    for (int i=0; i<N; i++) {
150        const FileRec& g = snapshot.valueAt(i);
151        if (!g.deleted) {
152            const String8& name = snapshot.keyAt(i);
153            bytesWritten += sizeof(FileState) + round_up(name.length());
154            fileCount++;
155        }
156    }
157
158    LOGP("write_snapshot_file fd=%d\n", fd);
159
160    int amt;
161    SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten };
162
163    amt = write(fd, &header, sizeof(header));
164    if (amt != sizeof(header)) {
165        ALOGW("write_snapshot_file error writing header %s", strerror(errno));
166        return errno;
167    }
168
169    for (int i=0; i<N; i++) {
170        FileRec r = snapshot.valueAt(i);
171        if (!r.deleted) {
172            const String8& name = snapshot.keyAt(i);
173            int nameLen = r.s.nameLen = name.length();
174
175            amt = write(fd, &r.s, sizeof(FileState));
176            if (amt != sizeof(FileState)) {
177                ALOGW("write_snapshot_file error writing header %s", strerror(errno));
178                return 1;
179            }
180
181            // filename is not NULL terminated, but it is padded
182            amt = write(fd, name.string(), nameLen);
183            if (amt != nameLen) {
184                ALOGW("write_snapshot_file error writing filename %s", strerror(errno));
185                return 1;
186            }
187            int paddingLen = ROUND_UP[nameLen % 4];
188            if (paddingLen != 0) {
189                int padding = 0xabababab;
190                amt = write(fd, &padding, paddingLen);
191                if (amt != paddingLen) {
192                    ALOGW("write_snapshot_file error writing %d bytes of filename padding %s",
193                            paddingLen, strerror(errno));
194                    return 1;
195                }
196            }
197        }
198    }
199
200    return 0;
201}
202
203static int
204write_delete_file(BackupDataWriter* dataStream, const String8& key)
205{
206    LOGP("write_delete_file %s\n", key.string());
207    return dataStream->WriteEntityHeader(key, -1);
208}
209
210static int
211write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key,
212        char const* realFilename)
213{
214    LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode);
215
216    const int bufsize = 4*1024;
217    int err;
218    int amt;
219    int fileSize;
220    int bytesLeft;
221    file_metadata_v1 metadata;
222
223    char* buf = (char*)malloc(bufsize);
224
225    fileSize = lseek(fd, 0, SEEK_END);
226    lseek(fd, 0, SEEK_SET);
227
228    if (sizeof(metadata) != 16) {
229        ALOGE("ERROR: metadata block is the wrong size!");
230    }
231
232    bytesLeft = fileSize + sizeof(metadata);
233    err = dataStream->WriteEntityHeader(key, bytesLeft);
234    if (err != 0) {
235        free(buf);
236        return err;
237    }
238
239    // store the file metadata first
240    metadata.version = tolel(CURRENT_METADATA_VERSION);
241    metadata.mode = tolel(mode);
242    metadata.undefined_1 = metadata.undefined_2 = 0;
243    err = dataStream->WriteEntityData(&metadata, sizeof(metadata));
244    if (err != 0) {
245        free(buf);
246        return err;
247    }
248    bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now
249
250    // now store the file content
251    while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) {
252        bytesLeft -= amt;
253        if (bytesLeft < 0) {
254            amt += bytesLeft; // Plus a negative is minus.  Don't write more than we promised.
255        }
256        err = dataStream->WriteEntityData(buf, amt);
257        if (err != 0) {
258            free(buf);
259            return err;
260        }
261    }
262    if (bytesLeft != 0) {
263        if (bytesLeft > 0) {
264            // Pad out the space we promised in the buffer.  We can't corrupt the buffer,
265            // even though the data we're sending is probably bad.
266            memset(buf, 0, bufsize);
267            while (bytesLeft > 0) {
268                amt = bytesLeft < bufsize ? bytesLeft : bufsize;
269                bytesLeft -= amt;
270                err = dataStream->WriteEntityData(buf, amt);
271                if (err != 0) {
272                    free(buf);
273                    return err;
274                }
275            }
276        }
277        ALOGE("write_update_file size mismatch for %s. expected=%d actual=%d."
278                " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft);
279    }
280
281    free(buf);
282    return NO_ERROR;
283}
284
285static int
286write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename)
287{
288    int err;
289    struct stat st;
290
291    err = stat(realFilename, &st);
292    if (err < 0) {
293        return errno;
294    }
295
296    int fd = open(realFilename, O_RDONLY);
297    if (fd == -1) {
298        return errno;
299    }
300
301    err = write_update_file(dataStream, fd, st.st_mode, key, realFilename);
302    close(fd);
303    return err;
304}
305
306static int
307compute_crc32(const char* file, FileRec* out) {
308    int fd = open(file, O_RDONLY);
309    if (fd < 0) {
310        return -1;
311    }
312
313    const int bufsize = 4*1024;
314    int amt;
315
316    char* buf = (char*)malloc(bufsize);
317    int crc = crc32(0L, Z_NULL, 0);
318
319    lseek(fd, 0, SEEK_SET);
320
321    while ((amt = read(fd, buf, bufsize)) != 0) {
322        crc = crc32(crc, (Bytef*)buf, amt);
323    }
324
325    close(fd);
326    free(buf);
327
328    out->s.crc32 = crc;
329    return NO_ERROR;
330}
331
332int
333back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
334        char const* const* files, char const* const* keys, int fileCount)
335{
336    int err;
337    KeyedVector<String8,FileState> oldSnapshot;
338    KeyedVector<String8,FileRec> newSnapshot;
339
340    if (oldSnapshotFD != -1) {
341        err = read_snapshot_file(oldSnapshotFD, &oldSnapshot);
342        if (err != 0) {
343            // On an error, treat this as a full backup.
344            oldSnapshot.clear();
345        }
346    }
347
348    for (int i=0; i<fileCount; i++) {
349        String8 key(keys[i]);
350        FileRec r;
351        char const* file = files[i];
352        r.file = file;
353        struct stat st;
354
355        err = stat(file, &st);
356        if (err != 0) {
357            // not found => treat as deleted
358            continue;
359        } else {
360            r.deleted = false;
361            r.s.modTime_sec = st.st_mtime;
362            r.s.modTime_nsec = 0; // workaround sim breakage
363            //r.s.modTime_nsec = st.st_mtime_nsec;
364            r.s.mode = st.st_mode;
365            r.s.size = st.st_size;
366
367            if (newSnapshot.indexOfKey(key) >= 0) {
368                LOGP("back_up_files key already in use '%s'", key.string());
369                return -1;
370            }
371
372            // compute the CRC
373            if (compute_crc32(file, &r) != NO_ERROR) {
374                ALOGW("Unable to open file %s", file);
375                continue;
376            }
377        }
378        newSnapshot.add(key, r);
379    }
380
381    int n = 0;
382    int N = oldSnapshot.size();
383    int m = 0;
384    int M = newSnapshot.size();
385
386    while (n<N && m<M) {
387        const String8& p = oldSnapshot.keyAt(n);
388        const String8& q = newSnapshot.keyAt(m);
389        FileRec& g = newSnapshot.editValueAt(m);
390        int cmp = p.compare(q);
391        if (cmp < 0) {
392            // file present in oldSnapshot, but not present in newSnapshot
393            LOGP("file removed: %s", p.string());
394            write_delete_file(dataStream, p);
395            n++;
396        } else if (cmp > 0) {
397            // file added
398            LOGP("file added: %s crc=0x%08x", g.file.string(), g.s.crc32);
399            write_update_file(dataStream, q, g.file.string());
400            m++;
401        } else {
402            // same file exists in both old and new; check whether to update
403            const FileState& f = oldSnapshot.valueAt(n);
404
405            LOGP("%s", q.string());
406            LOGP("  old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
407                    f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32);
408            LOGP("  new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
409                    g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32);
410            if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
411                    || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) {
412                int fd = open(g.file.string(), O_RDONLY);
413                if (fd < 0) {
414                    ALOGE("Unable to read file for backup: %s", g.file.string());
415                } else {
416                    write_update_file(dataStream, fd, g.s.mode, p, g.file.string());
417                    close(fd);
418                }
419            }
420            n++;
421            m++;
422        }
423    }
424
425    // these were deleted
426    while (n<N) {
427        write_delete_file(dataStream, oldSnapshot.keyAt(n));
428        n++;
429    }
430
431    // these were added
432    while (m<M) {
433        const String8& q = newSnapshot.keyAt(m);
434        FileRec& g = newSnapshot.editValueAt(m);
435        write_update_file(dataStream, q, g.file.string());
436        m++;
437    }
438
439    err = write_snapshot_file(newSnapshotFD, newSnapshot);
440
441    return 0;
442}
443
444static void calc_tar_checksum(char* buf, size_t buf_size) {
445    // [ 148 :   8 ] checksum -- to be calculated with this field as space chars
446    memset(buf + 148, ' ', 8);
447
448    uint16_t sum = 0;
449    for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) {
450        sum += *p;
451    }
452
453    // Now write the real checksum value:
454    // [ 148 :   8 ]  checksum: 6 octal digits [leading zeroes], NUL, SPC
455    snprintf(buf + 148, buf_size - 148, "%06o", sum); // the trailing space is
456                                                      // already in place
457}
458
459// Returns number of bytes written
460static int write_pax_header_entry(char* buf, size_t buf_size,
461                                  const char* key, const char* value) {
462    // start with the size of "1 key=value\n"
463    int len = strlen(key) + strlen(value) + 4;
464    if (len > 9) len++;
465    if (len > 99) len++;
466    if (len > 999) len++;
467    // since PATH_MAX is 4096 we don't expect to have to generate any single
468    // header entry longer than 9999 characters
469
470    return snprintf(buf, buf_size, "%d %s=%s\n", len, key, value);
471}
472
473// Wire format to the backup manager service is chunked:  each chunk is prefixed by
474// a 4-byte count of its size.  A chunk size of zero (four zero bytes) indicates EOD.
475void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) {
476    uint32_t chunk_size_no = htonl(size);
477    writer->WriteEntityData(&chunk_size_no, 4);
478    if (size != 0) writer->WriteEntityData(buffer, size);
479}
480
481int write_tarfile(const String8& packageName, const String8& domain,
482        const String8& rootpath, const String8& filepath, off_t* outSize,
483        BackupDataWriter* writer)
484{
485    // In the output stream everything is stored relative to the root
486    const char* relstart = filepath.string() + rootpath.length();
487    if (*relstart == '/') relstart++;     // won't be true when path == rootpath
488    String8 relpath(relstart);
489
490    // If relpath is empty, it means this is the top of one of the standard named
491    // domain directories, so we should just skip it
492    if (relpath.length() == 0) {
493        *outSize = 0;
494        return 0;
495    }
496
497    // Too long a name for the ustar format?
498    //    "apps/" + packagename + '/' + domainpath < 155 chars
499    //    relpath < 100 chars
500    bool needExtended = false;
501    if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) {
502        needExtended = true;
503    }
504
505    // Non-7bit-clean path also means needing pax extended format
506    if (!needExtended) {
507        for (size_t i = 0; i < filepath.length(); i++) {
508            if ((filepath[i] & 0x80) != 0) {
509                needExtended = true;
510                break;
511            }
512        }
513    }
514
515    int err = 0;
516    struct stat64 s;
517    if (lstat64(filepath.string(), &s) != 0) {
518        err = errno;
519        ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
520        return err;
521    }
522
523    // very large files need a pax extended size header
524    if (s.st_size > 077777777777LL) {
525        needExtended = true;
526    }
527
528    String8 fullname;   // for pax later on
529    String8 prefix;
530
531    const int isdir = S_ISDIR(s.st_mode);
532    if (isdir) s.st_size = 0;   // directories get no actual data in the tar stream
533
534    // Report the size, including a rough tar overhead estimation: 512 bytes for the
535    // overall tar file-block header, plus 2 blocks if using the pax extended format,
536    // plus the raw content size rounded up to a multiple of 512.
537    *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512);
538
539    // Measure case: we've returned the size; now return without moving data
540    if (!writer) return 0;
541
542    // !!! TODO: use mmap when possible to avoid churning the buffer cache
543    // !!! TODO: this will break with symlinks; need to use readlink(2)
544    int fd = open(filepath.string(), O_RDONLY);
545    if (fd < 0) {
546        err = errno;
547        ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
548        return err;
549    }
550
551    // read/write up to this much at a time.
552    const size_t BUFSIZE = 32 * 1024;
553    char* buf = (char *)calloc(1,BUFSIZE);
554    const size_t PAXHEADER_OFFSET = 512;
555    const size_t PAXHEADER_SIZE = 512;
556    const size_t PAXDATA_SIZE = BUFSIZE - (PAXHEADER_SIZE + PAXHEADER_OFFSET);
557    char* const paxHeader = buf + PAXHEADER_OFFSET; // use a different chunk of
558                                                    // it as separate scratch
559    char* const paxData = paxHeader + PAXHEADER_SIZE;
560
561    if (buf == NULL) {
562        ALOGE("Out of mem allocating transfer buffer");
563        err = ENOMEM;
564        goto done;
565    }
566
567    // Magic fields for the ustar file format
568    strcat(buf + 257, "ustar");
569    strcat(buf + 263, "00");
570
571    // [ 265 : 32 ] user name, ignored on restore
572    // [ 297 : 32 ] group name, ignored on restore
573
574    // [ 100 :   8 ] file mode
575    snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT);
576
577    // [ 108 :   8 ] uid -- ignored in Android format; uids are remapped at restore time
578    // [ 116 :   8 ] gid -- ignored in Android format
579    snprintf(buf + 108, 8, "0%lo", (unsigned long)s.st_uid);
580    snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
581
582    // [ 124 :  12 ] file size in bytes
583    snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
584
585    // [ 136 :  12 ] last mod time as a UTC time_t
586    snprintf(buf + 136, 12, "%0lo", (unsigned long)s.st_mtime);
587
588    // [ 156 :   1 ] link/file type
589    uint8_t type;
590    if (isdir) {
591        type = '5';     // tar magic: '5' == directory
592    } else if (S_ISREG(s.st_mode)) {
593        type = '0';     // tar magic: '0' == normal file
594    } else {
595        ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
596        goto cleanup;
597    }
598    buf[156] = type;
599
600    // [ 157 : 100 ] name of linked file [not implemented]
601
602    {
603        // Prefix and main relative path.  Path lengths have been preflighted.
604        if (packageName.length() > 0) {
605            prefix = "apps/";
606            prefix += packageName;
607        }
608        if (domain.length() > 0) {
609            prefix.appendPath(domain);
610        }
611
612        // pax extended means we don't put in a prefix field, and put a different
613        // string in the basic name field.  We can also construct the full path name
614        // out of the substrings we've now built.
615        fullname = prefix;
616        fullname.appendPath(relpath);
617
618        // ustar:
619        //    [   0 : 100 ]; file name/path
620        //    [ 345 : 155 ] filename path prefix
621        // We only use the prefix area if fullname won't fit in the path
622        if (fullname.length() > 100) {
623            strncpy(buf, relpath.string(), 100);
624            strncpy(buf + 345, prefix.string(), 155);
625        } else {
626            strncpy(buf, fullname.string(), 100);
627        }
628    }
629
630    // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used
631
632    ALOGI("   Name: %s", fullname.string());
633
634    // If we're using a pax extended header, build & write that here; lengths are
635    // already preflighted
636    if (needExtended) {
637        char sizeStr[32];   // big enough for a 64-bit unsigned value in decimal
638
639        // construct the pax extended header data block
640        memset(paxData, 0, PAXDATA_SIZE);
641
642        // size header -- calc len in digits by actually rendering the number
643        // to a string - brute force but simple
644        int paxLen = 0;
645        snprintf(sizeStr, sizeof(sizeStr), "%lld", (long long)s.st_size);
646        paxLen += write_pax_header_entry(paxData, PAXDATA_SIZE, "size", sizeStr);
647
648        // fullname was generated above with the ustar paths
649        paxLen += write_pax_header_entry(paxData + paxLen, PAXDATA_SIZE - paxLen,
650                "path", fullname.string());
651
652        // Now we know how big the pax data is
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)paxLen);
668
669        // Checksum and write the pax block header
670        calc_tar_checksum(paxHeader, PAXHEADER_SIZE);
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, BUFSIZE);
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    size_t actualSize;
1207    bool done;
1208    int type;
1209    ssize_t nRead;
1210
1211    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
1212
1213    err = reader.ReadNextHeader(&done, &type);
1214    if (done) {
1215        fprintf(stderr, "should not be done yet\n");
1216        goto finished;
1217    }
1218    if (err != 0) {
1219        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
1220        goto finished;
1221    }
1222    if (type != BACKUP_HEADER_ENTITY_V1) {
1223        err = EINVAL;
1224        fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1);
1225    }
1226
1227    err = reader.ReadEntityHeader(&string, &actualSize);
1228    if (err != 0) {
1229        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
1230        goto finished;
1231    }
1232    if (string != str) {
1233        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
1234        err = EINVAL;
1235        goto finished;
1236    }
1237    if (actualSize != bufSize) {
1238        fprintf(stderr, "ReadEntityHeader expected dataSize %zu got %zu\n",
1239                bufSize, actualSize);
1240        err = EINVAL;
1241        goto finished;
1242    }
1243
1244    nRead = reader.ReadEntityData(buf, bufSize);
1245    if (nRead < 0) {
1246        err = reader.Status();
1247        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
1248        goto finished;
1249    }
1250
1251    if (0 != memcmp(buf, str, bufSize)) {
1252        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
1253                "%02x %02x %02x %02x  '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3],
1254                buf[0], buf[1], buf[2], buf[3]);
1255        err = EINVAL;
1256        goto finished;
1257    }
1258
1259    // The next read will confirm whether it got the right amount of data.
1260
1261finished:
1262    if (err != NO_ERROR) {
1263        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
1264    }
1265    free(buf);
1266    return err;
1267}
1268
1269int
1270backup_helper_test_data_reader()
1271{
1272    int err;
1273    int fd;
1274    const char* filename = SCRATCH_DIR "data_reader.data";
1275
1276    system("rm -r " SCRATCH_DIR);
1277    mkdir(SCRATCH_DIR, 0777);
1278    mkdir(SCRATCH_DIR "data", 0777);
1279
1280    fd = creat(filename, 0666);
1281    if (fd == -1) {
1282        fprintf(stderr, "error creating: %s\n", strerror(errno));
1283        return errno;
1284    }
1285
1286    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
1287    if (err != DATA_GOLDEN_FILE_SIZE) {
1288        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
1289        return errno;
1290    }
1291
1292    close(fd);
1293
1294    fd = open(filename, O_RDONLY);
1295    if (fd == -1) {
1296        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
1297                filename);
1298        return errno;
1299    }
1300
1301    {
1302        BackupDataReader reader(fd);
1303
1304        err = 0;
1305
1306        if (err == NO_ERROR) {
1307            err = test_read_header_and_entity(reader, "no_padding_");
1308        }
1309
1310        if (err == NO_ERROR) {
1311            err = test_read_header_and_entity(reader, "padded_to__3");
1312        }
1313
1314        if (err == NO_ERROR) {
1315            err = test_read_header_and_entity(reader, "padded_to_2__");
1316        }
1317
1318        if (err == NO_ERROR) {
1319            err = test_read_header_and_entity(reader, "padded_to1");
1320        }
1321    }
1322
1323    close(fd);
1324
1325    return err;
1326}
1327
1328static int
1329get_mod_time(const char* filename, struct timeval times[2])
1330{
1331    int err;
1332    struct stat64 st;
1333    err = stat64(filename, &st);
1334    if (err != 0) {
1335        fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno));
1336        return errno;
1337    }
1338
1339    times[0].tv_sec = st.st_atim.tv_sec;
1340    times[0].tv_usec = st.st_atim.tv_nsec / 1000;
1341
1342    times[1].tv_sec = st.st_mtim.tv_sec;
1343    times[1].tv_usec = st.st_mtim.tv_nsec / 1000;
1344
1345    return 0;
1346}
1347
1348int
1349backup_helper_test_files()
1350{
1351    int err;
1352    int oldSnapshotFD;
1353    int dataStreamFD;
1354    int newSnapshotFD;
1355
1356    system("rm -r " SCRATCH_DIR);
1357    mkdir(SCRATCH_DIR, 0777);
1358    mkdir(SCRATCH_DIR "data", 0777);
1359
1360    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1361    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1362    write_text_file(SCRATCH_DIR "data/d", "d\ndd\n");
1363    write_text_file(SCRATCH_DIR "data/e", "e\nee\n");
1364    write_text_file(SCRATCH_DIR "data/f", "f\nff\n");
1365    write_text_file(SCRATCH_DIR "data/h", "h\nhh\n");
1366
1367    char const* files_before[] = {
1368        SCRATCH_DIR "data/b",
1369        SCRATCH_DIR "data/c",
1370        SCRATCH_DIR "data/d",
1371        SCRATCH_DIR "data/e",
1372        SCRATCH_DIR "data/f"
1373    };
1374
1375    char const* keys_before[] = {
1376        "data/b",
1377        "data/c",
1378        "data/d",
1379        "data/e",
1380        "data/f"
1381    };
1382
1383    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
1384    if (dataStreamFD == -1) {
1385        fprintf(stderr, "error creating: %s\n", strerror(errno));
1386        return errno;
1387    }
1388
1389    newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
1390    if (newSnapshotFD == -1) {
1391        fprintf(stderr, "error creating: %s\n", strerror(errno));
1392        return errno;
1393    }
1394
1395    {
1396        BackupDataWriter dataStream(dataStreamFD);
1397
1398        err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5);
1399        if (err != 0) {
1400            return err;
1401        }
1402    }
1403
1404    close(dataStreamFD);
1405    close(newSnapshotFD);
1406
1407    sleep(3);
1408
1409    struct timeval d_times[2];
1410    struct timeval e_times[2];
1411
1412    err = get_mod_time(SCRATCH_DIR "data/d", d_times);
1413    err |= get_mod_time(SCRATCH_DIR "data/e", e_times);
1414    if (err != 0) {
1415        return err;
1416    }
1417
1418    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1419    unlink(SCRATCH_DIR "data/c");
1420    write_text_file(SCRATCH_DIR "data/c", "c\ncc\n");
1421    write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n");
1422    utimes(SCRATCH_DIR "data/d", d_times);
1423    write_text_file(SCRATCH_DIR "data/e", "z\nzz\n");
1424    utimes(SCRATCH_DIR "data/e", e_times);
1425    write_text_file(SCRATCH_DIR "data/g", "g\ngg\n");
1426    unlink(SCRATCH_DIR "data/f");
1427
1428    char const* files_after[] = {
1429        SCRATCH_DIR "data/a", // added
1430        SCRATCH_DIR "data/b", // same
1431        SCRATCH_DIR "data/c", // different mod time
1432        SCRATCH_DIR "data/d", // different size (same mod time)
1433        SCRATCH_DIR "data/e", // different contents (same mod time, same size)
1434        SCRATCH_DIR "data/g"  // added
1435    };
1436
1437    char const* keys_after[] = {
1438        "data/a", // added
1439        "data/b", // same
1440        "data/c", // different mod time
1441        "data/d", // different size (same mod time)
1442        "data/e", // different contents (same mod time, same size)
1443        "data/g"  // added
1444    };
1445
1446    oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY);
1447    if (oldSnapshotFD == -1) {
1448        fprintf(stderr, "error opening: %s\n", strerror(errno));
1449        return errno;
1450    }
1451
1452    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
1453    if (dataStreamFD == -1) {
1454        fprintf(stderr, "error creating: %s\n", strerror(errno));
1455        return errno;
1456    }
1457
1458    newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
1459    if (newSnapshotFD == -1) {
1460        fprintf(stderr, "error creating: %s\n", strerror(errno));
1461        return errno;
1462    }
1463
1464    {
1465        BackupDataWriter dataStream(dataStreamFD);
1466
1467        err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6);
1468        if (err != 0) {
1469            return err;
1470        }
1471}
1472
1473    close(oldSnapshotFD);
1474    close(dataStreamFD);
1475    close(newSnapshotFD);
1476
1477    return 0;
1478}
1479
1480int
1481backup_helper_test_null_base()
1482{
1483    int err;
1484    int dataStreamFD;
1485    int newSnapshotFD;
1486
1487    system("rm -r " SCRATCH_DIR);
1488    mkdir(SCRATCH_DIR, 0777);
1489    mkdir(SCRATCH_DIR "data", 0777);
1490
1491    write_text_file(SCRATCH_DIR "data/a", "a\naa\n");
1492
1493    char const* files[] = {
1494        SCRATCH_DIR "data/a",
1495    };
1496
1497    char const* keys[] = {
1498        "a",
1499    };
1500
1501    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1502    if (dataStreamFD == -1) {
1503        fprintf(stderr, "error creating: %s\n", strerror(errno));
1504        return errno;
1505    }
1506
1507    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1508    if (newSnapshotFD == -1) {
1509        fprintf(stderr, "error creating: %s\n", strerror(errno));
1510        return errno;
1511    }
1512
1513    {
1514        BackupDataWriter dataStream(dataStreamFD);
1515
1516        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1517        if (err != 0) {
1518            return err;
1519        }
1520    }
1521
1522    close(dataStreamFD);
1523    close(newSnapshotFD);
1524
1525    return 0;
1526}
1527
1528int
1529backup_helper_test_missing_file()
1530{
1531    int err;
1532    int dataStreamFD;
1533    int newSnapshotFD;
1534
1535    system("rm -r " SCRATCH_DIR);
1536    mkdir(SCRATCH_DIR, 0777);
1537    mkdir(SCRATCH_DIR "data", 0777);
1538
1539    write_text_file(SCRATCH_DIR "data/b", "b\nbb\n");
1540
1541    char const* files[] = {
1542        SCRATCH_DIR "data/a",
1543        SCRATCH_DIR "data/b",
1544        SCRATCH_DIR "data/c",
1545    };
1546
1547    char const* keys[] = {
1548        "a",
1549        "b",
1550        "c",
1551    };
1552
1553    dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666);
1554    if (dataStreamFD == -1) {
1555        fprintf(stderr, "error creating: %s\n", strerror(errno));
1556        return errno;
1557    }
1558
1559    newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666);
1560    if (newSnapshotFD == -1) {
1561        fprintf(stderr, "error creating: %s\n", strerror(errno));
1562        return errno;
1563    }
1564
1565    {
1566        BackupDataWriter dataStream(dataStreamFD);
1567
1568        err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1);
1569        if (err != 0) {
1570            return err;
1571        }
1572    }
1573
1574    close(dataStreamFD);
1575    close(newSnapshotFD);
1576
1577    return 0;
1578}
1579
1580
1581#endif // TEST_BACKUP_HELPERS
1582
1583}
1584