blob: b19c53343f057887a00d2a8f32600466ce97d4f0 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2007 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#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <sys/mount.h> // for _IOW, _IOR, mount()
24#include <sys/stat.h>
25#include <mtd/mtd-user.h>
26#undef NDEBUG
27#include <assert.h>
28
29#include "mtdutils.h"
30
Kra1o531679362014-06-20 19:06:06 +020031#ifdef RK3X
32 #include "rk3xhack.h"
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +010033#endif
34
Michael Bestas5f052552015-01-02 01:45:37 +020035#ifdef BYNAME
36static const char mtdprefix[] = "/dev/block/mtd/by-name/";
37#define MTD_BASENAME_OFFSET (sizeof(mtdprefix)-1)
38#endif
39
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080040struct MtdReadContext {
41 const MtdPartition *partition;
42 char *buffer;
43 size_t consumed;
44 int fd;
45};
46
47struct MtdWriteContext {
48 const MtdPartition *partition;
49 char *buffer;
50 size_t stored;
51 int fd;
Doug Zongker4c5f9f32010-01-12 16:18:33 -080052
53 off_t* bad_block_offsets;
54 int bad_block_alloc;
55 int bad_block_count;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080056};
57
58typedef struct {
59 MtdPartition *partitions;
60 int partitions_allocd;
61 int partition_count;
62} MtdState;
63
64static MtdState g_mtd_state = {
65 NULL, // partitions
66 0, // partitions_allocd
67 -1 // partition_count
68};
69
70#define MTD_PROC_FILENAME "/proc/mtd"
71
72int
73mtd_scan_partitions()
74{
75 char buf[2048];
76 const char *bufp;
77 int fd;
78 int i;
79 ssize_t nbytes;
80
81 if (g_mtd_state.partitions == NULL) {
82 const int nump = 32;
83 MtdPartition *partitions = malloc(nump * sizeof(*partitions));
84 if (partitions == NULL) {
85 errno = ENOMEM;
86 return -1;
87 }
88 g_mtd_state.partitions = partitions;
89 g_mtd_state.partitions_allocd = nump;
90 memset(partitions, 0, nump * sizeof(*partitions));
91 }
92 g_mtd_state.partition_count = 0;
93
94 /* Initialize all of the entries to make things easier later.
95 * (Lets us handle sparsely-numbered partitions, which
96 * may not even be possible.)
97 */
98 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
99 MtdPartition *p = &g_mtd_state.partitions[i];
100 if (p->name != NULL) {
101 free(p->name);
102 p->name = NULL;
103 }
104 p->device_index = -1;
105 }
106
107 /* Open and read the file contents.
108 */
109 fd = open(MTD_PROC_FILENAME, O_RDONLY);
110 if (fd < 0) {
111 goto bail;
112 }
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700113 nbytes = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf) - 1));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800114 close(fd);
115 if (nbytes < 0) {
116 goto bail;
117 }
118 buf[nbytes] = '\0';
119
120 /* Parse the contents of the file, which looks like:
121 *
122 * # cat /proc/mtd
123 * dev: size erasesize name
124 * mtd0: 00080000 00020000 "bootloader"
125 * mtd1: 00400000 00020000 "mfg_and_gsm"
126 * mtd2: 00400000 00020000 "0000000c"
127 * mtd3: 00200000 00020000 "0000000d"
128 * mtd4: 04000000 00020000 "system"
129 * mtd5: 03280000 00020000 "userdata"
130 */
131 bufp = buf;
132 while (nbytes > 0) {
133 int mtdnum, mtdsize, mtderasesize;
134 int matches;
135 char mtdname[64];
136 mtdname[0] = '\0';
137 mtdnum = -1;
138
139 matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]",
140 &mtdnum, &mtdsize, &mtderasesize, mtdname);
141 /* This will fail on the first line, which just contains
142 * column headers.
143 */
144 if (matches == 4) {
145 MtdPartition *p = &g_mtd_state.partitions[mtdnum];
146 p->device_index = mtdnum;
147 p->size = mtdsize;
148 p->erase_size = mtderasesize;
Michael Bestas5f052552015-01-02 01:45:37 +0200149#ifdef BYNAME
150 asprintf(&p->name, "%s%s", mtdprefix, mtdname);
151#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800152 p->name = strdup(mtdname);
Michael Bestas5f052552015-01-02 01:45:37 +0200153#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800154 if (p->name == NULL) {
155 errno = ENOMEM;
156 goto bail;
157 }
158 g_mtd_state.partition_count++;
159 }
160
161 /* Eat the line.
162 */
163 while (nbytes > 0 && *bufp != '\n') {
164 bufp++;
165 nbytes--;
166 }
167 if (nbytes > 0) {
168 bufp++;
169 nbytes--;
170 }
171 }
172
173 return g_mtd_state.partition_count;
174
175bail:
176 // keep "partitions" around so we can free the names on a rescan.
177 g_mtd_state.partition_count = -1;
178 return -1;
179}
180
181const MtdPartition *
182mtd_find_partition_by_name(const char *name)
183{
184 if (g_mtd_state.partitions != NULL) {
185 int i;
186 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
187 MtdPartition *p = &g_mtd_state.partitions[i];
188 if (p->device_index >= 0 && p->name != NULL) {
189 if (strcmp(p->name, name) == 0) {
190 return p;
191 }
Michael Bestas5f052552015-01-02 01:45:37 +0200192#ifdef BYNAME
193 if (strcmp(p->name+MTD_BASENAME_OFFSET, name) == 0) {
194 return p;
195 }
196#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800197 }
198 }
199 }
200 return NULL;
201}
202
203int
204mtd_mount_partition(const MtdPartition *partition, const char *mount_point,
205 const char *filesystem, int read_only)
206{
207 const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
208 char devname[64];
209 int rv = -1;
210
211 sprintf(devname, "/dev/block/mtdblock%d", partition->device_index);
212 if (!read_only) {
213 rv = mount(devname, mount_point, filesystem, flags, NULL);
214 }
215 if (read_only || rv < 0) {
216 rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0);
217 if (rv < 0) {
218 printf("Failed to mount %s on %s: %s\n",
219 devname, mount_point, strerror(errno));
220 } else {
221 printf("Mount %s on %s read-only\n", devname, mount_point);
222 }
223 }
224#if 1 //TODO: figure out why this is happening; remove include of stat.h
225 if (rv >= 0) {
226 /* For some reason, the x bits sometimes aren't set on the root
227 * of mounted volumes.
228 */
229 struct stat st;
230 rv = stat(mount_point, &st);
231 if (rv < 0) {
232 return rv;
233 }
234 mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
235 if (new_mode != st.st_mode) {
236printf("Fixing execute permissions for %s\n", mount_point);
237 rv = chmod(mount_point, new_mode);
238 if (rv < 0) {
239 printf("Couldn't fix permissions for %s: %s\n",
240 mount_point, strerror(errno));
241 }
242 }
243 }
244#endif
245 return rv;
246}
247
248int
249mtd_partition_info(const MtdPartition *partition,
250 size_t *total_size, size_t *erase_size, size_t *write_size)
251{
252 char mtddevname[32];
253 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
254 int fd = open(mtddevname, O_RDONLY);
255 if (fd < 0) return -1;
256
257 struct mtd_info_user mtd_info;
258 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
259 close(fd);
260 if (ret < 0) return -1;
261
262 if (total_size != NULL) *total_size = mtd_info.size;
263 if (erase_size != NULL) *erase_size = mtd_info.erasesize;
264 if (write_size != NULL) *write_size = mtd_info.writesize;
265 return 0;
266}
267
268MtdReadContext *mtd_read_partition(const MtdPartition *partition)
269{
270 MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext));
271 if (ctx == NULL) return NULL;
272
273 ctx->buffer = malloc(partition->erase_size);
274 if (ctx->buffer == NULL) {
275 free(ctx);
276 return NULL;
277 }
278
279 char mtddevname[32];
280 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
281 ctx->fd = open(mtddevname, O_RDONLY);
282 if (ctx->fd < 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800283 free(ctx->buffer);
Christian Lindeberg862c83b2011-01-19 12:22:41 +0100284 free(ctx);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800285 return NULL;
286 }
287
288 ctx->partition = partition;
289 ctx->consumed = partition->erase_size;
290 return ctx;
291}
292
293static int read_block(const MtdPartition *partition, int fd, char *data)
294{
295 struct mtd_ecc_stats before, after;
296 if (ioctl(fd, ECCGETSTATS, &before)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700297 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800298 return -1;
299 }
300
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700301 loff_t pos = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
302 if (pos == -1) {
303 printf("mtd: read_block: couldn't SEEK_CUR: %s\n", strerror(errno));
304 return -1;
305 }
Doug Zongker17a47092009-12-14 18:03:27 -0800306
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800307 ssize_t size = partition->erase_size;
Doug Zongker17a47092009-12-14 18:03:27 -0800308 int mgbb;
309
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800310 while (pos + size <= (int) partition->size) {
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700311 if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos ||
312 TEMP_FAILURE_RETRY(read(fd, data, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700313 printf("mtd: read error at 0x%08llx (%s)\n",
Tao Bao5701d582015-09-24 10:56:48 -0700314 (long long)pos, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800315 } else if (ioctl(fd, ECCGETSTATS, &after)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700316 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800317 return -1;
318 } else if (after.failed != before.failed) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700319 printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
Tao Bao5701d582015-09-24 10:56:48 -0700320 after.corrected - before.corrected,
321 after.failed - before.failed, (long long)pos);
Doug Zongker5d6309e2010-11-03 14:31:01 -0700322 // copy the comparison baseline for the next read.
323 memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
Doug Zongker17a47092009-12-14 18:03:27 -0800324 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
325 fprintf(stderr,
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700326 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx: %s\n",
Tao Bao5701d582015-09-24 10:56:48 -0700327 mgbb, (long long)pos, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800328 } else {
Doug Zongker61ba7a82010-09-12 13:36:40 -0700329 return 0; // Success!
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800330 }
331
332 pos += partition->erase_size;
333 }
334
335 errno = ENOSPC;
336 return -1;
337}
338
339ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
340{
Dees_Troy51a0e822012-09-05 15:24:24 -0400341 ssize_t read = 0;
342 while (read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800343 if (ctx->consumed < ctx->partition->erase_size) {
344 size_t avail = ctx->partition->erase_size - ctx->consumed;
345 size_t copy = len - read < avail ? len - read : avail;
346 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
347 ctx->consumed += copy;
348 read += copy;
349 }
350
351 // Read complete blocks directly into the user's buffer
352 while (ctx->consumed == ctx->partition->erase_size &&
353 len - read >= ctx->partition->erase_size) {
354 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
355 read += ctx->partition->erase_size;
356 }
357
Dees_Troy51a0e822012-09-05 15:24:24 -0400358 if (read >= (int)len) {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700359 return read;
360 }
361
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800362 // Read the next block into the buffer
Dees_Troy51a0e822012-09-05 15:24:24 -0400363 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800364 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
365 ctx->consumed = 0;
366 }
367 }
368
369 return read;
370}
371
372void mtd_read_close(MtdReadContext *ctx)
373{
374 close(ctx->fd);
375 free(ctx->buffer);
376 free(ctx);
377}
378
379MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
380{
381 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
382 if (ctx == NULL) return NULL;
383
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800384 ctx->bad_block_offsets = NULL;
385 ctx->bad_block_alloc = 0;
386 ctx->bad_block_count = 0;
387
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800388 ctx->buffer = malloc(partition->erase_size);
389 if (ctx->buffer == NULL) {
390 free(ctx);
391 return NULL;
392 }
393
394 char mtddevname[32];
395 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
396 ctx->fd = open(mtddevname, O_RDWR);
397 if (ctx->fd < 0) {
398 free(ctx->buffer);
399 free(ctx);
400 return NULL;
401 }
402
403 ctx->partition = partition;
404 ctx->stored = 0;
405 return ctx;
406}
407
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800408static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
409 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
410 ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
411 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
412 ctx->bad_block_alloc * sizeof(off_t));
413 }
414 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
415}
416
417static int write_block(MtdWriteContext *ctx, const char *data)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800418{
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800419 const MtdPartition *partition = ctx->partition;
420 int fd = ctx->fd;
421
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700422 off_t pos = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_CUR));
423 if (pos == (off_t) -1) {
424 printf("mtd: write_block: couldn't SEEK_CUR: %s\n", strerror(errno));
425 return -1;
426 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800427
428 ssize_t size = partition->erase_size;
429 while (pos + size <= (int) partition->size) {
430 loff_t bpos = pos;
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700431 int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
432 if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800433 add_bad_block_offset(ctx, pos);
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700434 fprintf(stderr,
Elliott Hughes1fdd4522015-03-23 13:33:57 -0700435 "mtd: not writing bad block at 0x%08lx (ret %d): %s\n",
436 pos, ret, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800437 pos += partition->erase_size;
438 continue; // Don't try to erase known factory-bad blocks.
439 }
440
441 struct erase_info_user erase_info;
442 erase_info.start = pos;
443 erase_info.length = size;
444 int retry;
445 for (retry = 0; retry < 2; ++retry) {
Kra1o531679362014-06-20 19:06:06 +0200446#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100447 if (rk30_zero_out(fd, pos, size) < 0) {
448 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
449 pos, strerror(errno));
450 continue;
451 }
452#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800453 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700454 printf("mtd: erase failure at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800455 pos, strerror(errno));
456 continue;
457 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100458#endif
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700459 if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
460 TEMP_FAILURE_RETRY(write(fd, data, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700461 printf("mtd: write error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800462 pos, strerror(errno));
463 }
464
465 char verify[size];
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700466 if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
467 TEMP_FAILURE_RETRY(read(fd, verify, size)) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700468 printf("mtd: re-read error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800469 pos, strerror(errno));
470 continue;
471 }
472 if (memcmp(data, verify, size) != 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700473 printf("mtd: verification error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800474 pos, strerror(errno));
475 continue;
476 }
477
478 if (retry > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700479 printf("mtd: wrote block after %d retries\n", retry);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800480 }
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700481 printf("mtd: successfully wrote block at %lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800482 return 0; // Success!
483 }
484
485 // Try to erase it once more as we give up on this block
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800486 add_bad_block_offset(ctx, pos);
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700487 printf("mtd: skipping write block at 0x%08lx\n", pos);
Kra1o531679362014-06-20 19:06:06 +0200488#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100489 rk30_zero_out(fd, pos, size);
490#else
Dees Troybb4c0cb2013-11-02 20:25:14 +0000491
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800492 ioctl(fd, MEMERASE, &erase_info);
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100493#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800494 pos += partition->erase_size;
495 }
496
497 // Ran out of space on the device
498 errno = ENOSPC;
499 return -1;
500}
501
502ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
503{
504 size_t wrote = 0;
505 while (wrote < len) {
506 // Coalesce partial writes into complete blocks
507 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
508 size_t avail = ctx->partition->erase_size - ctx->stored;
509 size_t copy = len - wrote < avail ? len - wrote : avail;
510 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
511 ctx->stored += copy;
512 wrote += copy;
513 }
514
515 // If a complete block was accumulated, write it
516 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800517 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800518 ctx->stored = 0;
519 }
520
521 // Write complete blocks directly from the user's buffer
522 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800523 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800524 wrote += ctx->partition->erase_size;
525 }
526 }
527
528 return wrote;
529}
530
531off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
532{
533 // Zero-pad and write any pending data to get us to a block boundary
534 if (ctx->stored > 0) {
535 size_t zero = ctx->partition->erase_size - ctx->stored;
536 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800537 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800538 ctx->stored = 0;
539 }
540
Elliott Hughes2f5feed2015-04-28 17:24:24 -0700541 off_t pos = TEMP_FAILURE_RETRY(lseek(ctx->fd, 0, SEEK_CUR));
542 if ((off_t) pos == (off_t) -1) {
543 printf("mtd_erase_blocks: couldn't SEEK_CUR: %s\n", strerror(errno));
544 return -1;
545 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800546
547 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
548 if (blocks < 0) blocks = total;
549 if (blocks > total) {
550 errno = ENOSPC;
551 return -1;
552 }
553
554 // Erase the specified number of blocks
555 while (blocks-- > 0) {
556 loff_t bpos = pos;
557 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700558 printf("mtd: not erasing bad block at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800559 pos += ctx->partition->erase_size;
560 continue; // Don't try to erase known factory-bad blocks.
561 }
562
563 struct erase_info_user erase_info;
564 erase_info.start = pos;
565 erase_info.length = ctx->partition->erase_size;
Kra1o531679362014-06-20 19:06:06 +0200566#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100567 if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
568 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
569 }
570#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800571 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700572 printf("mtd: erase failure at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800573 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100574#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800575 pos += ctx->partition->erase_size;
576 }
577
578 return pos;
579}
580
581int mtd_write_close(MtdWriteContext *ctx)
582{
583 int r = 0;
584 // Make sure any pending data gets written
585 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
586 if (close(ctx->fd)) r = -1;
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800587 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800588 free(ctx->buffer);
589 free(ctx);
590 return r;
591}
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800592
593/* Return the offset of the first good block at or after pos (which
594 * might be pos itself).
595 */
596off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
597 int i;
598 for (i = 0; i < ctx->bad_block_count; ++i) {
599 if (ctx->bad_block_offsets[i] == pos) {
600 pos += ctx->partition->erase_size;
601 } else if (ctx->bad_block_offsets[i] > pos) {
602 return pos;
603 }
604 }
605 return pos;
606}
Dees_Troy51a0e822012-09-05 15:24:24 -0400607
Ethan Yonker58f21322018-08-24 11:17:36 -0500608#define MTD_BLOCK_SIZE 2048
609#define SPARE_SIZE (MTD_BLOCK_SIZE >> 5)
Dees_Troy51a0e822012-09-05 15:24:24 -0400610#define HEADER_SIZE 2048
611
612int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
613{
Dees_Troy51a0e822012-09-05 15:24:24 -0400614 FILE* f = fopen(filename, "rb");
615 if (f == NULL) {
616 fprintf(stderr, "error opening %s", filename);
617 return -1;
618 }
619
620 if (mtd_scan_partitions() <= 0)
621 {
622 fprintf(stderr, "error scanning partitions");
623 return -1;
624 }
625 const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
626 if (mtd == NULL)
627 {
628 fprintf(stderr, "can't find %s partition", partition_name);
629 return -1;
630 }
631
632 int fd = open(filename, O_RDONLY);
633 if (fd < 0)
634 {
635 printf("error opening %s", filename);
636 return -1;
637 }
638
639 MtdWriteContext* ctx = mtd_write_partition(mtd);
640 if (ctx == NULL) {
641 printf("error writing %s", partition_name);
642 return -1;
643 }
644
645 int success = 1;
646 char* buffer = malloc(BUFSIZ);
647 int read;
648 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
649 int wrote = mtd_write_data(ctx, buffer, read);
650 success = success && (wrote == read);
651 }
652 free(buffer);
653 fclose(f);
654
655 if (!success) {
656 fprintf(stderr, "error writing %s", partition_name);
657 return -1;
658 }
659
660 if (mtd_erase_blocks(ctx, -1) == -1) {
661 fprintf(stderr, "error erasing blocks of %s\n", partition_name);
662 }
663 if (mtd_write_close(ctx) != 0) {
664 fprintf(stderr, "error closing write of %s\n", partition_name);
665 }
666 printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
667 return 0;
668}
669
670
671int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
672{
673 MtdReadContext *in;
674 const MtdPartition *partition;
Ethan Yonker58f21322018-08-24 11:17:36 -0500675 char buf[MTD_BLOCK_SIZE + SPARE_SIZE];
Dees_Troy51a0e822012-09-05 15:24:24 -0400676 size_t partition_size;
Dees_Troy51a0e822012-09-05 15:24:24 -0400677 size_t total;
678 int fd;
679 int wrote;
680 int len;
681
682 if (mtd_scan_partitions() <= 0)
683 {
684 printf("error scanning partitions");
685 return -1;
686 }
687
688 partition = mtd_find_partition_by_name(partition_name);
689 if (partition == NULL)
690 {
691 printf("can't find %s partition", partition_name);
692 return -1;
693 }
694
695 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
696 printf("can't get info of partition %s", partition_name);
697 return -1;
698 }
699
700 if (!strcmp(filename, "-")) {
701 fd = fileno(stdout);
702 }
703 else {
704 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
705 }
706
707 if (fd < 0)
708 {
709 printf("error opening %s", filename);
710 return -1;
711 }
712
713 in = mtd_read_partition(partition);
714 if (in == NULL) {
715 close(fd);
716 unlink(filename);
717 printf("error opening %s: %s\n", partition_name, strerror(errno));
718 return -1;
719 }
720
721 total = 0;
Ethan Yonker58f21322018-08-24 11:17:36 -0500722 while ((len = mtd_read_data(in, buf, MTD_BLOCK_SIZE)) > 0) {
Dees_Troy51a0e822012-09-05 15:24:24 -0400723 wrote = write(fd, buf, len);
724 if (wrote != len) {
725 close(fd);
726 unlink(filename);
727 printf("error writing %s", filename);
728 return -1;
729 }
Ethan Yonker58f21322018-08-24 11:17:36 -0500730 total += MTD_BLOCK_SIZE;
Dees_Troy51a0e822012-09-05 15:24:24 -0400731 }
732
733 mtd_read_close(in);
734
735 if (close(fd)) {
736 unlink(filename);
737 printf("error closing %s", filename);
738 return -1;
739 }
740 return 0;
741}
742
743int cmd_mtd_erase_raw_partition(const char *partition_name)
744{
745 MtdWriteContext *out;
746 size_t erased;
Dees_Troy51a0e822012-09-05 15:24:24 -0400747
748 if (mtd_scan_partitions() <= 0)
749 {
750 printf("error scanning partitions");
751 return -1;
752 }
753 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
754 if (p == NULL)
755 {
756 printf("can't find %s partition", partition_name);
757 return -1;
758 }
759
760 out = mtd_write_partition(p);
761 if (out == NULL)
762 {
763 printf("could not estabilish write context for %s", partition_name);
764 return -1;
765 }
766
767 // do the actual erase, -1 = full partition erase
768 erased = mtd_erase_blocks(out, -1);
769
770 // erased = bytes erased, if zero, something borked
771 if (!erased)
772 {
773 printf("error erasing %s", partition_name);
774 return -1;
775 }
776
777 return 0;
778}
779
Ethan Yonker58f21322018-08-24 11:17:36 -0500780int cmd_mtd_erase_partition(const char *partition, const char *filesystem __unused)
Dees_Troy51a0e822012-09-05 15:24:24 -0400781{
782 return cmd_mtd_erase_raw_partition(partition);
783}
784
785
Ethan Yonker58f21322018-08-24 11:17:36 -0500786int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem __unused, int read_only)
Dees_Troy51a0e822012-09-05 15:24:24 -0400787{
788 mtd_scan_partitions();
789 const MtdPartition *p;
790 p = mtd_find_partition_by_name(partition);
791 if (p == NULL) {
792 return -1;
793 }
794 return mtd_mount_partition(p, mount_point, filesystem, read_only);
795}
796
797int cmd_mtd_get_partition_device(const char *partition, char *device)
798{
799 mtd_scan_partitions();
Ethan Yonker58f21322018-08-24 11:17:36 -0500800 const MtdPartition *p = mtd_find_partition_by_name(partition);
Dees_Troy51a0e822012-09-05 15:24:24 -0400801 if (p == NULL)
802 return -1;
803 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
804 return 0;
Ethan Yonker58f21322018-08-24 11:17:36 -0500805}