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