Loading include/utils/BackupHelpers.h +12 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,14 @@ public: ~BackupDataWriter(); status_t WriteEntityHeader(const String8& key, size_t dataSize); /* Note: WriteEntityData will write arbitrary data into the file without * validation or a previously-supplied header. The full backup implementation * uses it this way to generate a controlled binary stream that is not * entity-structured. If the implementation here is changed, either this * use case must remain valid, or the full backup implementation should be * adjusted to use some other appropriate mechanism. */ status_t WriteEntityData(const void* data, size_t size); void SetKeyPrefix(const String8& keyPrefix); Loading Loading @@ -103,7 +111,7 @@ public: bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); status_t SkipEntityData(); // must be called with the pointer at the begining of the data. status_t SkipEntityData(); // must be called with the pointer at the beginning of the data. ssize_t ReadEntityData(void* data, size_t size); private: Loading @@ -126,6 +134,9 @@ private: int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); int write_tarfile(const String8& packageName, const String8& domain, const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream); class RestoreHelperBase { public: Loading libs/utils/BackupData.cpp +14 −2 Original line number Diff line number Diff line Loading @@ -20,12 +20,15 @@ #include <utils/ByteOrder.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <cutils/log.h> namespace android { static const bool DEBUG = false; /* * File Format (v1): * Loading Loading @@ -75,6 +78,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; Loading Loading @@ -107,8 +111,8 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } if (false) { LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), if (DEBUG) { LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } Loading @@ -121,6 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; Loading @@ -128,6 +133,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; Loading @@ -145,7 +151,12 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size); if (m_status != NO_ERROR) { if (DEBUG) { LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); } return m_status; } Loading @@ -155,6 +166,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; Loading libs/utils/BackupHelpers.cpp +178 −0 Original line number Diff line number Diff line Loading @@ -442,6 +442,184 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } // Utility function, equivalent to stpcpy(): perform a strcpy, but instead of // returning the initial dest, return a pointer to the trailing NUL. static char* strcpy_ptr(char* dest, const char* str) { if (dest && str) { while ((*dest = *str) != 0) { dest++; str++; } } return dest; } int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { // In the output stream everything is stored relative to the root const char* relstart = filepath.string() + rootpath.length(); if (*relstart == '/') relstart++; // won't be true when path == rootpath String8 relpath(relstart); // Too long a name for the ustar format? // "apps/" + packagename + '/' + domainpath < 155 chars // relpath < 100 chars if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { LOGE("Filename [%s] too long, skipping", relpath.string()); return -1; } int err = 0; struct stat64 s; if (lstat64(filepath.string(), &s) != 0) { err = errno; LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); return err; } const int isdir = S_ISDIR(s.st_mode); // !!! TODO: use mmap when possible to avoid churning the buffer cache // !!! TODO: this will break with symlinks; need to use readlink(2) int fd = open(filepath.string(), O_RDONLY); if (fd < 0) { err = errno; LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); return err; } // read/write up to this much at a time. const size_t BUFSIZE = 32 * 1024; char* buf = new char[BUFSIZE]; if (buf == NULL) { LOGE("Out of mem allocating transfer buffer"); err = ENOMEM; goto done; } // Good to go -- first construct the standard tar header at the start of the buffer memset(buf, 0, 512); // tar header is 512 bytes // Magic fields for the ustar file format strcat(buf + 257, "ustar"); strcat(buf + 263, "00"); { // Prefix and main relative path. Path lengths have been preflighted. // [ 345 : 155 ] filename path prefix [ustar] // // packagename and domain can each be empty. char* cp = buf + 345; if (packageName.length() > 0) { // it's an app; so prefix with "apps/packagename/" cp = strcpy_ptr(cp, "apps/"); cp = strcpy_ptr(cp, packageName.string()); } if (domain.length() > 0) { // only need a / if there was a package name if (packageName.length() > 0) *cp++ = '/'; cp = strcpy_ptr(cp, domain.string()); } // [ 0 : 100 ]; file name/path strncpy(buf, relpath.string(), 100); LOGI(" Name: %s/%s", buf + 345, buf); } // [ 100 : 8 ] file mode snprintf(buf + 100, 8, "0%o", s.st_mode); // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time // [ 116 : 8 ] gid -- ignored in Android format snprintf(buf + 108, 8, "0%lo", s.st_uid); snprintf(buf + 116, 8, "0%lo", s.st_gid); // [ 124 : 12 ] file size in bytes snprintf(buf + 124, 12, "0%llo", s.st_size); // [ 136 : 12 ] last mod time as a UTC time_t snprintf(buf + 136, 12, "%0lo", s.st_mtime); // [ 148 : 8 ] checksum -- to be calculated with this field as space chars memset(buf + 148, ' ', 8); // [ 156 : 1 ] link/file type uint8_t type; if (isdir) { type = '5'; // tar magic: '5' == directory } else if (S_ISREG(s.st_mode)) { type = '0'; // tar magic: '0' == normal file } else { LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); goto cleanup; } buf[156] = type; // [ 157 : 100 ] name of linked file [not implemented] // Now go back and calculate the header checksum { uint16_t sum = 0; for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { sum += *p; } // Now write the real checksum value: // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC sprintf(buf + 148, "%06o", sum); // the trailing space is already in place } // Write the 512-byte tar file header block to the output writer->WriteEntityData(buf, 512); // Now write the file data itself, for real files. We honor tar's convention that // only full 512-byte blocks are sent to write(). if (!isdir) { off64_t toWrite = s.st_size; while (toWrite > 0) { size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; ssize_t nRead = read(fd, buf, toRead); if (nRead < 0) { err = errno; LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), err, strerror(err)); break; } else if (nRead == 0) { LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, filepath.string()); err = EIO; break; } // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This // depends on the OS guarantee that for ordinary files, read() will never return // less than the number of bytes requested. ssize_t partial = (nRead+512) % 512; if (partial > 0) { ssize_t remainder = 512 - partial; memset(buf + nRead, 0, remainder); nRead += remainder; } writer->WriteEntityData(buf, nRead); toWrite -= nRead; } } cleanup: delete [] buf; done: close(fd); return err; } // end tarfile #define RESTORE_BUF_SIZE (8*1024) RestoreHelperBase::RestoreHelperBase() Loading Loading
include/utils/BackupHelpers.h +12 −1 Original line number Diff line number Diff line Loading @@ -70,6 +70,14 @@ public: ~BackupDataWriter(); status_t WriteEntityHeader(const String8& key, size_t dataSize); /* Note: WriteEntityData will write arbitrary data into the file without * validation or a previously-supplied header. The full backup implementation * uses it this way to generate a controlled binary stream that is not * entity-structured. If the implementation here is changed, either this * use case must remain valid, or the full backup implementation should be * adjusted to use some other appropriate mechanism. */ status_t WriteEntityData(const void* data, size_t size); void SetKeyPrefix(const String8& keyPrefix); Loading Loading @@ -103,7 +111,7 @@ public: bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); status_t SkipEntityData(); // must be called with the pointer at the begining of the data. status_t SkipEntityData(); // must be called with the pointer at the beginning of the data. ssize_t ReadEntityData(void* data, size_t size); private: Loading @@ -126,6 +134,9 @@ private: int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); int write_tarfile(const String8& packageName, const String8& domain, const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream); class RestoreHelperBase { public: Loading
libs/utils/BackupData.cpp +14 −2 Original line number Diff line number Diff line Loading @@ -20,12 +20,15 @@ #include <utils/ByteOrder.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <cutils/log.h> namespace android { static const bool DEBUG = false; /* * File Format (v1): * Loading Loading @@ -75,6 +78,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; Loading Loading @@ -107,8 +111,8 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } if (false) { LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), if (DEBUG) { LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } Loading @@ -121,6 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; Loading @@ -128,6 +133,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; Loading @@ -145,7 +151,12 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size); if (m_status != NO_ERROR) { if (DEBUG) { LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); } return m_status; } Loading @@ -155,6 +166,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; Loading
libs/utils/BackupHelpers.cpp +178 −0 Original line number Diff line number Diff line Loading @@ -442,6 +442,184 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } // Utility function, equivalent to stpcpy(): perform a strcpy, but instead of // returning the initial dest, return a pointer to the trailing NUL. static char* strcpy_ptr(char* dest, const char* str) { if (dest && str) { while ((*dest = *str) != 0) { dest++; str++; } } return dest; } int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { // In the output stream everything is stored relative to the root const char* relstart = filepath.string() + rootpath.length(); if (*relstart == '/') relstart++; // won't be true when path == rootpath String8 relpath(relstart); // Too long a name for the ustar format? // "apps/" + packagename + '/' + domainpath < 155 chars // relpath < 100 chars if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { LOGE("Filename [%s] too long, skipping", relpath.string()); return -1; } int err = 0; struct stat64 s; if (lstat64(filepath.string(), &s) != 0) { err = errno; LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); return err; } const int isdir = S_ISDIR(s.st_mode); // !!! TODO: use mmap when possible to avoid churning the buffer cache // !!! TODO: this will break with symlinks; need to use readlink(2) int fd = open(filepath.string(), O_RDONLY); if (fd < 0) { err = errno; LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); return err; } // read/write up to this much at a time. const size_t BUFSIZE = 32 * 1024; char* buf = new char[BUFSIZE]; if (buf == NULL) { LOGE("Out of mem allocating transfer buffer"); err = ENOMEM; goto done; } // Good to go -- first construct the standard tar header at the start of the buffer memset(buf, 0, 512); // tar header is 512 bytes // Magic fields for the ustar file format strcat(buf + 257, "ustar"); strcat(buf + 263, "00"); { // Prefix and main relative path. Path lengths have been preflighted. // [ 345 : 155 ] filename path prefix [ustar] // // packagename and domain can each be empty. char* cp = buf + 345; if (packageName.length() > 0) { // it's an app; so prefix with "apps/packagename/" cp = strcpy_ptr(cp, "apps/"); cp = strcpy_ptr(cp, packageName.string()); } if (domain.length() > 0) { // only need a / if there was a package name if (packageName.length() > 0) *cp++ = '/'; cp = strcpy_ptr(cp, domain.string()); } // [ 0 : 100 ]; file name/path strncpy(buf, relpath.string(), 100); LOGI(" Name: %s/%s", buf + 345, buf); } // [ 100 : 8 ] file mode snprintf(buf + 100, 8, "0%o", s.st_mode); // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time // [ 116 : 8 ] gid -- ignored in Android format snprintf(buf + 108, 8, "0%lo", s.st_uid); snprintf(buf + 116, 8, "0%lo", s.st_gid); // [ 124 : 12 ] file size in bytes snprintf(buf + 124, 12, "0%llo", s.st_size); // [ 136 : 12 ] last mod time as a UTC time_t snprintf(buf + 136, 12, "%0lo", s.st_mtime); // [ 148 : 8 ] checksum -- to be calculated with this field as space chars memset(buf + 148, ' ', 8); // [ 156 : 1 ] link/file type uint8_t type; if (isdir) { type = '5'; // tar magic: '5' == directory } else if (S_ISREG(s.st_mode)) { type = '0'; // tar magic: '0' == normal file } else { LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); goto cleanup; } buf[156] = type; // [ 157 : 100 ] name of linked file [not implemented] // Now go back and calculate the header checksum { uint16_t sum = 0; for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { sum += *p; } // Now write the real checksum value: // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC sprintf(buf + 148, "%06o", sum); // the trailing space is already in place } // Write the 512-byte tar file header block to the output writer->WriteEntityData(buf, 512); // Now write the file data itself, for real files. We honor tar's convention that // only full 512-byte blocks are sent to write(). if (!isdir) { off64_t toWrite = s.st_size; while (toWrite > 0) { size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; ssize_t nRead = read(fd, buf, toRead); if (nRead < 0) { err = errno; LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), err, strerror(err)); break; } else if (nRead == 0) { LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, filepath.string()); err = EIO; break; } // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This // depends on the OS guarantee that for ordinary files, read() will never return // less than the number of bytes requested. ssize_t partial = (nRead+512) % 512; if (partial > 0) { ssize_t remainder = 512 - partial; memset(buf + nRead, 0, remainder); nRead += remainder; } writer->WriteEntityData(buf, nRead); toWrite -= nRead; } } cleanup: delete [] buf; done: close(fd); return err; } // end tarfile #define RESTORE_BUF_SIZE (8*1024) RestoreHelperBase::RestoreHelperBase() Loading