blob: 032b04bac1f2d27889aea7a3933a65ea21213104 [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
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +010031#ifdef RK3066
32 #include "rk30hack.h"
33#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)) {
289 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
290 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) {
300 fprintf(stderr, "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)) {
303 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
304 return -1;
305 } else if (after.failed != before.failed) {
Doug Zongker17a47092009-12-14 18:03:27 -0800306 fprintf(stderr, "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) {
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100430#ifdef RK3066
431 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) {
438 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
439 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) {
445 fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
446 pos, strerror(errno));
447 }
448
449 char verify[size];
450 if (lseek(fd, pos, SEEK_SET) != pos ||
451 read(fd, verify, size) != size) {
452 fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
453 pos, strerror(errno));
454 continue;
455 }
456 if (memcmp(data, verify, size) != 0) {
457 fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
458 pos, strerror(errno));
459 continue;
460 }
461
462 if (retry > 0) {
463 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
464 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400465 fprintf(stderr, "mtd: successfully wrote block at %llx\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);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800471 fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100472#ifdef RK3066
473 rk30_zero_out(fd, pos, size);
474#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800475 ioctl(fd, MEMERASE, &erase_info);
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100476#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800477 pos += partition->erase_size;
478 }
479
480 // Ran out of space on the device
481 errno = ENOSPC;
482 return -1;
483}
484
485ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
486{
487 size_t wrote = 0;
488 while (wrote < len) {
489 // Coalesce partial writes into complete blocks
490 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
491 size_t avail = ctx->partition->erase_size - ctx->stored;
492 size_t copy = len - wrote < avail ? len - wrote : avail;
493 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
494 ctx->stored += copy;
495 wrote += copy;
496 }
497
498 // If a complete block was accumulated, write it
499 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800500 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800501 ctx->stored = 0;
502 }
503
504 // Write complete blocks directly from the user's buffer
505 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800506 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800507 wrote += ctx->partition->erase_size;
508 }
509 }
510
511 return wrote;
512}
513
514off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
515{
516 // Zero-pad and write any pending data to get us to a block boundary
517 if (ctx->stored > 0) {
518 size_t zero = ctx->partition->erase_size - ctx->stored;
519 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800520 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800521 ctx->stored = 0;
522 }
523
524 off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
525 if ((off_t) pos == (off_t) -1) return pos;
526
527 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
528 if (blocks < 0) blocks = total;
529 if (blocks > total) {
530 errno = ENOSPC;
531 return -1;
532 }
533
534 // Erase the specified number of blocks
535 while (blocks-- > 0) {
536 loff_t bpos = pos;
537 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
538 fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos);
539 pos += ctx->partition->erase_size;
540 continue; // Don't try to erase known factory-bad blocks.
541 }
542
543 struct erase_info_user erase_info;
544 erase_info.start = pos;
545 erase_info.length = ctx->partition->erase_size;
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100546#ifdef RK3066
547 if (rk30_zero_out(ctx->fd, pos, ctx->partition->erase_size) < 0) {
548 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
549 }
550#else
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800551 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
552 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
553 }
Sergey 'Jin' Bostandzhyan80a90ed2013-01-04 02:29:03 +0100554#endif
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800555 pos += ctx->partition->erase_size;
556 }
557
558 return pos;
559}
560
561int mtd_write_close(MtdWriteContext *ctx)
562{
563 int r = 0;
564 // Make sure any pending data gets written
565 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
566 if (close(ctx->fd)) r = -1;
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800567 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800568 free(ctx->buffer);
569 free(ctx);
570 return r;
571}
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800572
573/* Return the offset of the first good block at or after pos (which
574 * might be pos itself).
575 */
576off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
577 int i;
578 for (i = 0; i < ctx->bad_block_count; ++i) {
579 if (ctx->bad_block_offsets[i] == pos) {
580 pos += ctx->partition->erase_size;
581 } else if (ctx->bad_block_offsets[i] > pos) {
582 return pos;
583 }
584 }
585 return pos;
586}
Dees_Troy51a0e822012-09-05 15:24:24 -0400587
588#define BLOCK_SIZE 2048
589#define SPARE_SIZE (BLOCK_SIZE >> 5)
590#define HEADER_SIZE 2048
591
592int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
593{
594 const MtdPartition *ptn;
595 MtdWriteContext *write;
596 void *data;
597
598 FILE* f = fopen(filename, "rb");
599 if (f == NULL) {
600 fprintf(stderr, "error opening %s", filename);
601 return -1;
602 }
603
604 if (mtd_scan_partitions() <= 0)
605 {
606 fprintf(stderr, "error scanning partitions");
607 return -1;
608 }
609 const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
610 if (mtd == NULL)
611 {
612 fprintf(stderr, "can't find %s partition", partition_name);
613 return -1;
614 }
615
616 int fd = open(filename, O_RDONLY);
617 if (fd < 0)
618 {
619 printf("error opening %s", filename);
620 return -1;
621 }
622
623 MtdWriteContext* ctx = mtd_write_partition(mtd);
624 if (ctx == NULL) {
625 printf("error writing %s", partition_name);
626 return -1;
627 }
628
629 int success = 1;
630 char* buffer = malloc(BUFSIZ);
631 int read;
632 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
633 int wrote = mtd_write_data(ctx, buffer, read);
634 success = success && (wrote == read);
635 }
636 free(buffer);
637 fclose(f);
638
639 if (!success) {
640 fprintf(stderr, "error writing %s", partition_name);
641 return -1;
642 }
643
644 if (mtd_erase_blocks(ctx, -1) == -1) {
645 fprintf(stderr, "error erasing blocks of %s\n", partition_name);
646 }
647 if (mtd_write_close(ctx) != 0) {
648 fprintf(stderr, "error closing write of %s\n", partition_name);
649 }
650 printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
651 return 0;
652}
653
654
655int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
656{
657 MtdReadContext *in;
658 const MtdPartition *partition;
659 char buf[BLOCK_SIZE + SPARE_SIZE];
660 size_t partition_size;
661 size_t read_size;
662 size_t total;
663 int fd;
664 int wrote;
665 int len;
666
667 if (mtd_scan_partitions() <= 0)
668 {
669 printf("error scanning partitions");
670 return -1;
671 }
672
673 partition = mtd_find_partition_by_name(partition_name);
674 if (partition == NULL)
675 {
676 printf("can't find %s partition", partition_name);
677 return -1;
678 }
679
680 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
681 printf("can't get info of partition %s", partition_name);
682 return -1;
683 }
684
685 if (!strcmp(filename, "-")) {
686 fd = fileno(stdout);
687 }
688 else {
689 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
690 }
691
692 if (fd < 0)
693 {
694 printf("error opening %s", filename);
695 return -1;
696 }
697
698 in = mtd_read_partition(partition);
699 if (in == NULL) {
700 close(fd);
701 unlink(filename);
702 printf("error opening %s: %s\n", partition_name, strerror(errno));
703 return -1;
704 }
705
706 total = 0;
707 while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
708 wrote = write(fd, buf, len);
709 if (wrote != len) {
710 close(fd);
711 unlink(filename);
712 printf("error writing %s", filename);
713 return -1;
714 }
715 total += BLOCK_SIZE;
716 }
717
718 mtd_read_close(in);
719
720 if (close(fd)) {
721 unlink(filename);
722 printf("error closing %s", filename);
723 return -1;
724 }
725 return 0;
726}
727
728int cmd_mtd_erase_raw_partition(const char *partition_name)
729{
730 MtdWriteContext *out;
731 size_t erased;
732 size_t total_size;
733 size_t erase_size;
734
735 if (mtd_scan_partitions() <= 0)
736 {
737 printf("error scanning partitions");
738 return -1;
739 }
740 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
741 if (p == NULL)
742 {
743 printf("can't find %s partition", partition_name);
744 return -1;
745 }
746
747 out = mtd_write_partition(p);
748 if (out == NULL)
749 {
750 printf("could not estabilish write context for %s", partition_name);
751 return -1;
752 }
753
754 // do the actual erase, -1 = full partition erase
755 erased = mtd_erase_blocks(out, -1);
756
757 // erased = bytes erased, if zero, something borked
758 if (!erased)
759 {
760 printf("error erasing %s", partition_name);
761 return -1;
762 }
763
764 return 0;
765}
766
767int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
768{
769 return cmd_mtd_erase_raw_partition(partition);
770}
771
772
773int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
774{
775 mtd_scan_partitions();
776 const MtdPartition *p;
777 p = mtd_find_partition_by_name(partition);
778 if (p == NULL) {
779 return -1;
780 }
781 return mtd_mount_partition(p, mount_point, filesystem, read_only);
782}
783
784int cmd_mtd_get_partition_device(const char *partition, char *device)
785{
786 mtd_scan_partitions();
787 MtdPartition *p = mtd_find_partition_by_name(partition);
788 if (p == NULL)
789 return -1;
790 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
791 return 0;
792}