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