@@ -57,10 +57,9 @@ std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
57
57
58
58
bool WriteToImageFile (int fd, const LpMetadata& input) {
59
59
std::string geometry = SerializeGeometry (input.geometry );
60
- std::string padding (LP_METADATA_GEOMETRY_SIZE - geometry.size (), ' \0 ' );
61
60
std::string metadata = SerializeMetadata (input);
62
61
63
- std::string everything = geometry + padding + metadata;
62
+ std::string everything = geometry + metadata;
64
63
65
64
if (!android::base::WriteFully (fd, everything.data (), everything.size ())) {
66
65
PERROR << __PRETTY_FUNCTION__ << " write " << everything.size () << " bytes failed" ;
@@ -83,26 +82,29 @@ bool WriteToImageFile(const char* file, const LpMetadata& input) {
83
82
// to do this when the data pointers are all in one place.
84
83
class SparseBuilder {
85
84
public:
86
- explicit SparseBuilder (const LpMetadata& metadata);
85
+ SparseBuilder (const LpMetadata& metadata, uint32_t block_size );
87
86
88
87
bool Build ();
89
88
bool Export (const char * file);
90
89
bool IsValid () const { return file_ != nullptr ; }
91
90
92
91
private:
93
- bool AddData (const std::string& blob, uint32_t block);
92
+ bool AddData (const std::string& blob, uint64_t sector);
93
+ bool SectorToBlock (uint64_t sector, uint32_t * block);
94
94
95
95
const LpMetadata& metadata_;
96
96
const LpMetadataGeometry& geometry_;
97
+ uint32_t block_size_;
97
98
std::unique_ptr<sparse_file, decltype (&sparse_file_destroy)> file_;
98
- std::string geometry_blob_ ;
99
- std::string metadata_blob_ ;
99
+ std::string primary_blob_ ;
100
+ std::string backup_blob_ ;
100
101
};
101
102
102
- SparseBuilder::SparseBuilder (const LpMetadata& metadata)
103
+ SparseBuilder::SparseBuilder (const LpMetadata& metadata, uint32_t block_size )
103
104
: metadata_(metadata),
104
105
geometry_ (metadata.geometry),
105
- file_(sparse_file_new(LP_SECTOR_SIZE, geometry_.block_device_size), sparse_file_destroy) {}
106
+ block_size_(block_size),
107
+ file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy) {}
106
108
107
109
bool SparseBuilder::Export (const char * file) {
108
110
android::base::unique_fd fd (open (file, O_CREAT | O_RDWR | O_TRUNC, 0644 ));
@@ -119,7 +121,11 @@ bool SparseBuilder::Export(const char* file) {
119
121
return true ;
120
122
}
121
123
122
- bool SparseBuilder::AddData (const std::string& blob, uint32_t block) {
124
+ bool SparseBuilder::AddData (const std::string& blob, uint64_t sector) {
125
+ uint32_t block;
126
+ if (!SectorToBlock (sector, &block)) {
127
+ return false ;
128
+ }
123
129
void * data = const_cast <char *>(blob.data ());
124
130
int ret = sparse_file_add_data (file_.get (), data, blob.size (), block);
125
131
if (ret != 0 ) {
@@ -129,54 +135,70 @@ bool SparseBuilder::AddData(const std::string& blob, uint32_t block) {
129
135
return true ;
130
136
}
131
137
132
- bool SparseBuilder::Build () {
133
- geometry_blob_ = SerializeGeometry (geometry_);
134
- geometry_blob_.resize (LP_METADATA_GEOMETRY_SIZE);
135
- if (!AddData (geometry_blob_, 0 )) {
138
+ bool SparseBuilder::SectorToBlock (uint64_t sector, uint32_t * block) {
139
+ // The caller must ensure that the metadata has an alignment that is a
140
+ // multiple of the block size. liblp will take care of the rest, ensuring
141
+ // that all partitions are on an aligned boundary. Therefore all writes
142
+ // should be block-aligned, and if they are not, the table was misconfigured.
143
+ // Note that the default alignment is 1MiB, which is a multiple of the
144
+ // default block size (4096).
145
+ if ((sector * LP_SECTOR_SIZE) % block_size_ != 0 ) {
146
+ LERROR << " sector " << sector << " is not aligned to block size " << block_size_;
136
147
return false ;
137
148
}
149
+ *block = (sector * LP_SECTOR_SIZE) / block_size_;
150
+ return true ;
151
+ }
138
152
139
- // Metadata immediately follows geometry, and we write the same metadata
140
- // to all slots.
141
- uint32_t metadata_block = LP_METADATA_GEOMETRY_SIZE / LP_SECTOR_SIZE;
142
- metadata_blob_ = SerializeMetadata (metadata_);
153
+ bool SparseBuilder::Build () {
154
+ std::string geometry_blob = SerializeGeometry (geometry_);
155
+ std::string metadata_blob = SerializeMetadata (metadata_);
156
+ metadata_blob.resize (geometry_.metadata_max_size );
157
+
158
+ std::string all_metadata;
143
159
for (size_t i = 0 ; i < geometry_.metadata_slot_count ; i++) {
144
- if (!AddData (metadata_blob_, metadata_block)) {
145
- return false ;
146
- }
147
- metadata_block += geometry_.metadata_max_size / LP_SECTOR_SIZE;
160
+ all_metadata += metadata_blob;
161
+ }
162
+
163
+ // Metadata immediately follows geometry, and we write the same metadata
164
+ // to all slots. Note that we don't bother trying to write skip chunks
165
+ // here since it's a small amount of data.
166
+ primary_blob_ = geometry_blob + all_metadata;
167
+ if (!AddData (primary_blob_, 0 )) {
168
+ return false ;
148
169
}
149
170
150
171
// The backup area contains all metadata slots, and then geometry. Similar
151
172
// to before we write the metadata to every slot.
152
173
int64_t backup_offset = GetBackupMetadataOffset (geometry_, 0 );
153
174
uint64_t backups_start = geometry_.block_device_size + backup_offset;
154
175
uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
155
- for (size_t i = 0 ; i < geometry_.metadata_slot_count ; i++) {
156
- if (!AddData (metadata_blob_, backup_sector)) {
157
- return false ;
158
- }
159
- backup_sector += geometry_.metadata_max_size / LP_SECTOR_SIZE;
160
- }
161
- if (!AddData (geometry_blob_, backup_sector)) {
176
+
177
+ backup_blob_ = all_metadata + geometry_blob;
178
+ if (!AddData (backup_blob_, backup_sector)) {
162
179
return false ;
163
180
}
164
181
return true ;
165
182
}
166
183
167
- bool WriteToSparseFile (const char * file, const LpMetadata& metadata) {
168
- uint64_t num_blocks =
169
- AlignTo (metadata.geometry .block_device_size , LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
184
+ bool WriteToSparseFile (const char * file, const LpMetadata& metadata, uint32_t block_size) {
185
+ if (block_size % LP_SECTOR_SIZE != 0 ) {
186
+ LERROR << " Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
187
+ return false ;
188
+ }
189
+ if (metadata.geometry .block_device_size % block_size != 0 ) {
190
+ LERROR << " Device size must be a multiple of the block size, " << block_size;
191
+ return false ;
192
+ }
193
+ uint64_t num_blocks = metadata.geometry .block_device_size % block_size;
170
194
if (num_blocks >= UINT_MAX) {
171
- // libsparse counts blocks in unsigned 32-bit integers, but our block
172
- // size is rather low (512 bytes), since we operate in sectors.
173
- // Therefore the maximum block device size we can represent with a
174
- // sparse file is 2TB for now.
195
+ // libsparse counts blocks in unsigned 32-bit integers, so we check to
196
+ // make sure we're not going to overflow.
175
197
LERROR << " Block device is too large to encode with libsparse." ;
176
198
return false ;
177
199
}
178
200
179
- SparseBuilder builder (metadata);
201
+ SparseBuilder builder (metadata, block_size );
180
202
if (!builder.IsValid ()) {
181
203
LERROR << " Could not allocate sparse file of size " << metadata.geometry .block_device_size ;
182
204
return false ;
0 commit comments