blob: 158c880588de1765f3c3ee5ef164dc7a3bb136f3 [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
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080035struct MtdReadContext {
36 const MtdPartition *partition;
37 char *buffer;
38 size_t consumed;
39 int fd;
40};
41
42struct MtdWriteContext {
43 const MtdPartition *partition;
44 char *buffer;
45 size_t stored;
46 int fd;
Doug Zongker4c5f9f32010-01-12 16:18:33 -080047
48 off_t* bad_block_offsets;
49 int bad_block_alloc;
50 int bad_block_count;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080051};
52
53typedef struct {
54 MtdPartition *partitions;
55 int partitions_allocd;
56 int partition_count;
57} MtdState;
58
59static MtdState g_mtd_state = {
60 NULL, // partitions
61 0, // partitions_allocd
62 -1 // partition_count
63};
64
65#define MTD_PROC_FILENAME "/proc/mtd"
66
67int
68mtd_scan_partitions()
69{
70 char buf[2048];
71 const char *bufp;
72 int fd;
73 int i;
74 ssize_t nbytes;
75
76 if (g_mtd_state.partitions == NULL) {
77 const int nump = 32;
78 MtdPartition *partitions = malloc(nump * sizeof(*partitions));
79 if (partitions == NULL) {
80 errno = ENOMEM;
81 return -1;
82 }
83 g_mtd_state.partitions = partitions;
84 g_mtd_state.partitions_allocd = nump;
85 memset(partitions, 0, nump * sizeof(*partitions));
86 }
87 g_mtd_state.partition_count = 0;
88
89 /* Initialize all of the entries to make things easier later.
90 * (Lets us handle sparsely-numbered partitions, which
91 * may not even be possible.)
92 */
93 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
94 MtdPartition *p = &g_mtd_state.partitions[i];
95 if (p->name != NULL) {
96 free(p->name);
97 p->name = NULL;
98 }
99 p->device_index = -1;
100 }
101
102 /* Open and read the file contents.
103 */
104 fd = open(MTD_PROC_FILENAME, O_RDONLY);
105 if (fd < 0) {
106 goto bail;
107 }
108 nbytes = read(fd, buf, sizeof(buf) - 1);
109 close(fd);
110 if (nbytes < 0) {
111 goto bail;
112 }
113 buf[nbytes] = '\0';
114
115 /* Parse the contents of the file, which looks like:
116 *
117 * # cat /proc/mtd
118 * dev: size erasesize name
119 * mtd0: 00080000 00020000 "bootloader"
120 * mtd1: 00400000 00020000 "mfg_and_gsm"
121 * mtd2: 00400000 00020000 "0000000c"
122 * mtd3: 00200000 00020000 "0000000d"
123 * mtd4: 04000000 00020000 "system"
124 * mtd5: 03280000 00020000 "userdata"
125 */
126 bufp = buf;
127 while (nbytes > 0) {
128 int mtdnum, mtdsize, mtderasesize;
129 int matches;
130 char mtdname[64];
131 mtdname[0] = '\0';
132 mtdnum = -1;
133
134 matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]",
135 &mtdnum, &mtdsize, &mtderasesize, mtdname);
136 /* This will fail on the first line, which just contains
137 * column headers.
138 */
139 if (matches == 4) {
140 MtdPartition *p = &g_mtd_state.partitions[mtdnum];
141 p->device_index = mtdnum;
142 p->size = mtdsize;
143 p->erase_size = mtderasesize;
144 p->name = strdup(mtdname);
145 if (p->name == NULL) {
146 errno = ENOMEM;
147 goto bail;
148 }
149 g_mtd_state.partition_count++;
150 }
151
152 /* Eat the line.
153 */
154 while (nbytes > 0 && *bufp != '\n') {
155 bufp++;
156 nbytes--;
157 }
158 if (nbytes > 0) {
159 bufp++;
160 nbytes--;
161 }
162 }
163
164 return g_mtd_state.partition_count;
165
166bail:
167 // keep "partitions" around so we can free the names on a rescan.
168 g_mtd_state.partition_count = -1;
169 return -1;
170}
171
172const MtdPartition *
173mtd_find_partition_by_name(const char *name)
174{
175 if (g_mtd_state.partitions != NULL) {
176 int i;
177 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
178 MtdPartition *p = &g_mtd_state.partitions[i];
179 if (p->device_index >= 0 && p->name != NULL) {
180 if (strcmp(p->name, name) == 0) {
181 return p;
182 }
183 }
184 }
185 }
186 return NULL;
187}
188
189int
190mtd_mount_partition(const MtdPartition *partition, const char *mount_point,
191 const char *filesystem, int read_only)
192{
193 const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
194 char devname[64];
195 int rv = -1;
196
197 sprintf(devname, "/dev/block/mtdblock%d", partition->device_index);
198 if (!read_only) {
199 rv = mount(devname, mount_point, filesystem, flags, NULL);
200 }
201 if (read_only || rv < 0) {
202 rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0);
203 if (rv < 0) {
204 printf("Failed to mount %s on %s: %s\n",
205 devname, mount_point, strerror(errno));
206 } else {
207 printf("Mount %s on %s read-only\n", devname, mount_point);
208 }
209 }
210#if 1 //TODO: figure out why this is happening; remove include of stat.h
211 if (rv >= 0) {
212 /* For some reason, the x bits sometimes aren't set on the root
213 * of mounted volumes.
214 */
215 struct stat st;
216 rv = stat(mount_point, &st);
217 if (rv < 0) {
218 return rv;
219 }
220 mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
221 if (new_mode != st.st_mode) {
222printf("Fixing execute permissions for %s\n", mount_point);
223 rv = chmod(mount_point, new_mode);
224 if (rv < 0) {
225 printf("Couldn't fix permissions for %s: %s\n",
226 mount_point, strerror(errno));
227 }
228 }
229 }
230#endif
231 return rv;
232}
233
234int
235mtd_partition_info(const MtdPartition *partition,
236 size_t *total_size, size_t *erase_size, size_t *write_size)
237{
238 char mtddevname[32];
239 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
240 int fd = open(mtddevname, O_RDONLY);
241 if (fd < 0) return -1;
242
243 struct mtd_info_user mtd_info;
244 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
245 close(fd);
246 if (ret < 0) return -1;
247
248 if (total_size != NULL) *total_size = mtd_info.size;
249 if (erase_size != NULL) *erase_size = mtd_info.erasesize;
250 if (write_size != NULL) *write_size = mtd_info.writesize;
251 return 0;
252}
253
254MtdReadContext *mtd_read_partition(const MtdPartition *partition)
255{
256 MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext));
257 if (ctx == NULL) return NULL;
258
259 ctx->buffer = malloc(partition->erase_size);
260 if (ctx->buffer == NULL) {
261 free(ctx);
262 return NULL;
263 }
264
265 char mtddevname[32];
266 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
267 ctx->fd = open(mtddevname, O_RDONLY);
268 if (ctx->fd < 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800269 free(ctx->buffer);
Christian Lindeberg862c83b2011-01-19 12:22:41 +0100270 free(ctx);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800271 return NULL;
272 }
273
274 ctx->partition = partition;
275 ctx->consumed = partition->erase_size;
276 return ctx;
277}
278
Doug Zongkerd12560a2010-09-14 14:25:48 -0700279// Seeks to a location in the partition. Don't mix with reads of
280// anything other than whole blocks; unpredictable things will result.
281void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) {
282 lseek64(ctx->fd, offset, SEEK_SET);
283}
284
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800285static int read_block(const MtdPartition *partition, int fd, char *data)
286{
287 struct mtd_ecc_stats before, after;
288 if (ioctl(fd, ECCGETSTATS, &before)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700289 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800290 return -1;
291 }
292
Doug Zongker17a47092009-12-14 18:03:27 -0800293 loff_t pos = lseek64(fd, 0, SEEK_CUR);
294
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800295 ssize_t size = partition->erase_size;
Doug Zongker17a47092009-12-14 18:03:27 -0800296 int mgbb;
297
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800298 while (pos + size <= (int) partition->size) {
Doug Zongker17a47092009-12-14 18:03:27 -0800299 if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700300 printf("mtd: read error at 0x%08llx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800301 pos, strerror(errno));
302 } else if (ioctl(fd, ECCGETSTATS, &after)) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700303 printf("mtd: ECCGETSTATS error (%s)\n", strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800304 return -1;
305 } else if (after.failed != before.failed) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700306 printf("mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800307 after.corrected - before.corrected,
308 after.failed - before.failed, pos);
Doug Zongker5d6309e2010-11-03 14:31:01 -0700309 // copy the comparison baseline for the next read.
310 memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
Doug Zongker17a47092009-12-14 18:03:27 -0800311 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
312 fprintf(stderr,
313 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
314 mgbb, pos, errno);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800315 } else {
Doug Zongker61ba7a82010-09-12 13:36:40 -0700316 return 0; // Success!
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800317 }
318
319 pos += partition->erase_size;
320 }
321
322 errno = ENOSPC;
323 return -1;
324}
325
326ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
327{
Dees_Troy51a0e822012-09-05 15:24:24 -0400328 ssize_t read = 0;
329 while (read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800330 if (ctx->consumed < ctx->partition->erase_size) {
331 size_t avail = ctx->partition->erase_size - ctx->consumed;
332 size_t copy = len - read < avail ? len - read : avail;
333 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
334 ctx->consumed += copy;
335 read += copy;
336 }
337
338 // Read complete blocks directly into the user's buffer
339 while (ctx->consumed == ctx->partition->erase_size &&
340 len - read >= ctx->partition->erase_size) {
341 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
342 read += ctx->partition->erase_size;
343 }
344
Dees_Troy51a0e822012-09-05 15:24:24 -0400345 if (read >= (int)len) {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700346 return read;
347 }
348
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800349 // Read the next block into the buffer
Dees_Troy51a0e822012-09-05 15:24:24 -0400350 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800351 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
352 ctx->consumed = 0;
353 }
354 }
355
356 return read;
357}
358
359void mtd_read_close(MtdReadContext *ctx)
360{
361 close(ctx->fd);
362 free(ctx->buffer);
363 free(ctx);
364}
365
366MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
367{
368 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
369 if (ctx == NULL) return NULL;
370
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800371 ctx->bad_block_offsets = NULL;
372 ctx->bad_block_alloc = 0;
373 ctx->bad_block_count = 0;
374
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800375 ctx->buffer = malloc(partition->erase_size);
376 if (ctx->buffer == NULL) {
377 free(ctx);
378 return NULL;
379 }
380
381 char mtddevname[32];
382 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
383 ctx->fd = open(mtddevname, O_RDWR);
384 if (ctx->fd < 0) {
385 free(ctx->buffer);
386 free(ctx);
387 return NULL;
388 }
389
390 ctx->partition = partition;
391 ctx->stored = 0;
392 return ctx;
393}
394
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800395static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
396 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
397 ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
398 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
399 ctx->bad_block_alloc * sizeof(off_t));
400 }
401 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
402}
403
404static int write_block(MtdWriteContext *ctx, const char *data)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800405{
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800406 const MtdPartition *partition = ctx->partition;
407 int fd = ctx->fd;
408
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800409 off_t pos = lseek(fd, 0, SEEK_CUR);
410 if (pos == (off_t) -1) return 1;
411
412 ssize_t size = partition->erase_size;
413 while (pos + size <= (int) partition->size) {
414 loff_t bpos = pos;
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700415 int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
416 if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800417 add_bad_block_offset(ctx, pos);
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700418 fprintf(stderr,
419 "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n",
420 pos, ret, errno);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800421 pos += partition->erase_size;
422 continue; // Don't try to erase known factory-bad blocks.
423 }
424
425 struct erase_info_user erase_info;
426 erase_info.start = pos;
427 erase_info.length = size;
428 int retry;
429 for (retry = 0; retry < 2; ++retry) {
Kra1o531679362014-06-20 19:06:06 +0200430#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100431 if (rk30_zero_out(fd, pos, size) < 0) {
432 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
433 pos, strerror(errno));
434 continue;
435 }
436#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800437 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700438 printf("mtd: erase failure at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800439 pos, strerror(errno));
440 continue;
441 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100442#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800443 if (lseek(fd, pos, SEEK_SET) != pos ||
444 write(fd, data, size) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700445 printf("mtd: write error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800446 pos, strerror(errno));
447 }
448
449 char verify[size];
450 if (lseek(fd, pos, SEEK_SET) != pos ||
451 read(fd, verify, size) != size) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700452 printf("mtd: re-read error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800453 pos, strerror(errno));
454 continue;
455 }
456 if (memcmp(data, verify, size) != 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700457 printf("mtd: verification error at 0x%08lx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800458 pos, strerror(errno));
459 continue;
460 }
461
462 if (retry > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700463 printf("mtd: wrote block after %d retries\n", retry);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800464 }
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700465 printf("mtd: successfully wrote block at %lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800466 return 0; // Success!
467 }
468
469 // Try to erase it once more as we give up on this block
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800470 add_bad_block_offset(ctx, pos);
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700471 printf("mtd: skipping write block at 0x%08lx\n", pos);
Kra1o531679362014-06-20 19:06:06 +0200472#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100473 rk30_zero_out(fd, pos, size);
474#else
Dees Troybb4c0cb2013-11-02 20:25:14 +0000475
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800476 ioctl(fd, MEMERASE, &erase_info);
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100477#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800478 pos += partition->erase_size;
479 }
480
481 // Ran out of space on the device
482 errno = ENOSPC;
483 return -1;
484}
485
486ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
487{
488 size_t wrote = 0;
489 while (wrote < len) {
490 // Coalesce partial writes into complete blocks
491 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
492 size_t avail = ctx->partition->erase_size - ctx->stored;
493 size_t copy = len - wrote < avail ? len - wrote : avail;
494 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
495 ctx->stored += copy;
496 wrote += copy;
497 }
498
499 // If a complete block was accumulated, write it
500 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800501 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800502 ctx->stored = 0;
503 }
504
505 // Write complete blocks directly from the user's buffer
506 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800507 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800508 wrote += ctx->partition->erase_size;
509 }
510 }
511
512 return wrote;
513}
514
515off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
516{
517 // Zero-pad and write any pending data to get us to a block boundary
518 if (ctx->stored > 0) {
519 size_t zero = ctx->partition->erase_size - ctx->stored;
520 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800521 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800522 ctx->stored = 0;
523 }
524
525 off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
526 if ((off_t) pos == (off_t) -1) return pos;
527
528 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
529 if (blocks < 0) blocks = total;
530 if (blocks > total) {
531 errno = ENOSPC;
532 return -1;
533 }
534
535 // Erase the specified number of blocks
536 while (blocks-- > 0) {
537 loff_t bpos = pos;
538 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700539 printf("mtd: not erasing bad block at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800540 pos += ctx->partition->erase_size;
541 continue; // Don't try to erase known factory-bad blocks.
542 }
543
544 struct erase_info_user erase_info;
545 erase_info.start = pos;
546 erase_info.length = ctx->partition->erase_size;
Kra1o531679362014-06-20 19:06:06 +0200547#ifdef RK3X
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100548 if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
549 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
550 }
551#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800552 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
Doug Zongkerfafc85b2013-07-09 12:29:45 -0700553 printf("mtd: erase failure at 0x%08lx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800554 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100555#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800556 pos += ctx->partition->erase_size;
557 }
558
559 return pos;
560}
561
562int mtd_write_close(MtdWriteContext *ctx)
563{
564 int r = 0;
565 // Make sure any pending data gets written
566 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
567 if (close(ctx->fd)) r = -1;
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800568 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800569 free(ctx->buffer);
570 free(ctx);
571 return r;
572}
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800573
574/* Return the offset of the first good block at or after pos (which
575 * might be pos itself).
576 */
577off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
578 int i;
579 for (i = 0; i < ctx->bad_block_count; ++i) {
580 if (ctx->bad_block_offsets[i] == pos) {
581 pos += ctx->partition->erase_size;
582 } else if (ctx->bad_block_offsets[i] > pos) {
583 return pos;
584 }
585 }
586 return pos;
587}
Dees_Troy51a0e822012-09-05 15:24:24 -0400588
589#define BLOCK_SIZE 2048
590#define SPARE_SIZE (BLOCK_SIZE >> 5)
591#define HEADER_SIZE 2048
592
593int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
594{
595 const MtdPartition *ptn;
596 MtdWriteContext *write;
597 void *data;
598
599 FILE* f = fopen(filename, "rb");
600 if (f == NULL) {
601 fprintf(stderr, "error opening %s", filename);
602 return -1;
603 }
604
605 if (mtd_scan_partitions() <= 0)
606 {
607 fprintf(stderr, "error scanning partitions");
608 return -1;
609 }
610 const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
611 if (mtd == NULL)
612 {
613 fprintf(stderr, "can't find %s partition", partition_name);
614 return -1;
615 }
616
617 int fd = open(filename, O_RDONLY);
618 if (fd < 0)
619 {
620 printf("error opening %s", filename);
621 return -1;
622 }
623
624 MtdWriteContext* ctx = mtd_write_partition(mtd);
625 if (ctx == NULL) {
626 printf("error writing %s", partition_name);
627 return -1;
628 }
629
630 int success = 1;
631 char* buffer = malloc(BUFSIZ);
632 int read;
633 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
634 int wrote = mtd_write_data(ctx, buffer, read);
635 success = success && (wrote == read);
636 }
637 free(buffer);
638 fclose(f);
639
640 if (!success) {
641 fprintf(stderr, "error writing %s", partition_name);
642 return -1;
643 }
644
645 if (mtd_erase_blocks(ctx, -1) == -1) {
646 fprintf(stderr, "error erasing blocks of %s\n", partition_name);
647 }
648 if (mtd_write_close(ctx) != 0) {
649 fprintf(stderr, "error closing write of %s\n", partition_name);
650 }
651 printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
652 return 0;
653}
654
655
656int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
657{
658 MtdReadContext *in;
659 const MtdPartition *partition;
660 char buf[BLOCK_SIZE + SPARE_SIZE];
661 size_t partition_size;
662 size_t read_size;
663 size_t total;
664 int fd;
665 int wrote;
666 int len;
667
668 if (mtd_scan_partitions() <= 0)
669 {
670 printf("error scanning partitions");
671 return -1;
672 }
673
674 partition = mtd_find_partition_by_name(partition_name);
675 if (partition == NULL)
676 {
677 printf("can't find %s partition", partition_name);
678 return -1;
679 }
680
681 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
682 printf("can't get info of partition %s", partition_name);
683 return -1;
684 }
685
686 if (!strcmp(filename, "-")) {
687 fd = fileno(stdout);
688 }
689 else {
690 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
691 }
692
693 if (fd < 0)
694 {
695 printf("error opening %s", filename);
696 return -1;
697 }
698
699 in = mtd_read_partition(partition);
700 if (in == NULL) {
701 close(fd);
702 unlink(filename);
703 printf("error opening %s: %s\n", partition_name, strerror(errno));
704 return -1;
705 }
706
707 total = 0;
708 while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
709 wrote = write(fd, buf, len);
710 if (wrote != len) {
711 close(fd);
712 unlink(filename);
713 printf("error writing %s", filename);
714 return -1;
715 }
716 total += BLOCK_SIZE;
717 }
718
719 mtd_read_close(in);
720
721 if (close(fd)) {
722 unlink(filename);
723 printf("error closing %s", filename);
724 return -1;
725 }
726 return 0;
727}
728
729int cmd_mtd_erase_raw_partition(const char *partition_name)
730{
731 MtdWriteContext *out;
732 size_t erased;
733 size_t total_size;
734 size_t erase_size;
735
736 if (mtd_scan_partitions() <= 0)
737 {
738 printf("error scanning partitions");
739 return -1;
740 }
741 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
742 if (p == NULL)
743 {
744 printf("can't find %s partition", partition_name);
745 return -1;
746 }
747
748 out = mtd_write_partition(p);
749 if (out == NULL)
750 {
751 printf("could not estabilish write context for %s", partition_name);
752 return -1;
753 }
754
755 // do the actual erase, -1 = full partition erase
756 erased = mtd_erase_blocks(out, -1);
757
758 // erased = bytes erased, if zero, something borked
759 if (!erased)
760 {
761 printf("error erasing %s", partition_name);
762 return -1;
763 }
764
765 return 0;
766}
767
768int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
769{
770 return cmd_mtd_erase_raw_partition(partition);
771}
772
773
774int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
775{
776 mtd_scan_partitions();
777 const MtdPartition *p;
778 p = mtd_find_partition_by_name(partition);
779 if (p == NULL) {
780 return -1;
781 }
782 return mtd_mount_partition(p, mount_point, filesystem, read_only);
783}
784
785int cmd_mtd_get_partition_device(const char *partition, char *device)
786{
787 mtd_scan_partitions();
788 MtdPartition *p = mtd_find_partition_by_name(partition);
789 if (p == NULL)
790 return -1;
791 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
792 return 0;
793}