blob: a76b8e992f509f241d6b463e5c628b2d39037508 [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
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080031struct MtdReadContext {
32 const MtdPartition *partition;
33 char *buffer;
34 size_t consumed;
35 int fd;
36};
37
38struct MtdWriteContext {
39 const MtdPartition *partition;
40 char *buffer;
41 size_t stored;
42 int fd;
Doug Zongker4c5f9f32010-01-12 16:18:33 -080043
44 off_t* bad_block_offsets;
45 int bad_block_alloc;
46 int bad_block_count;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080047};
48
49typedef struct {
50 MtdPartition *partitions;
51 int partitions_allocd;
52 int partition_count;
53} MtdState;
54
55static MtdState g_mtd_state = {
56 NULL, // partitions
57 0, // partitions_allocd
58 -1 // partition_count
59};
60
61#define MTD_PROC_FILENAME "/proc/mtd"
62
63int
64mtd_scan_partitions()
65{
66 char buf[2048];
67 const char *bufp;
68 int fd;
69 int i;
70 ssize_t nbytes;
71
72 if (g_mtd_state.partitions == NULL) {
73 const int nump = 32;
74 MtdPartition *partitions = malloc(nump * sizeof(*partitions));
75 if (partitions == NULL) {
76 errno = ENOMEM;
77 return -1;
78 }
79 g_mtd_state.partitions = partitions;
80 g_mtd_state.partitions_allocd = nump;
81 memset(partitions, 0, nump * sizeof(*partitions));
82 }
83 g_mtd_state.partition_count = 0;
84
85 /* Initialize all of the entries to make things easier later.
86 * (Lets us handle sparsely-numbered partitions, which
87 * may not even be possible.)
88 */
89 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
90 MtdPartition *p = &g_mtd_state.partitions[i];
91 if (p->name != NULL) {
92 free(p->name);
93 p->name = NULL;
94 }
95 p->device_index = -1;
96 }
97
98 /* Open and read the file contents.
99 */
100 fd = open(MTD_PROC_FILENAME, O_RDONLY);
101 if (fd < 0) {
102 goto bail;
103 }
104 nbytes = read(fd, buf, sizeof(buf) - 1);
105 close(fd);
106 if (nbytes < 0) {
107 goto bail;
108 }
109 buf[nbytes] = '\0';
110
111 /* Parse the contents of the file, which looks like:
112 *
113 * # cat /proc/mtd
114 * dev: size erasesize name
115 * mtd0: 00080000 00020000 "bootloader"
116 * mtd1: 00400000 00020000 "mfg_and_gsm"
117 * mtd2: 00400000 00020000 "0000000c"
118 * mtd3: 00200000 00020000 "0000000d"
119 * mtd4: 04000000 00020000 "system"
120 * mtd5: 03280000 00020000 "userdata"
121 */
122 bufp = buf;
123 while (nbytes > 0) {
124 int mtdnum, mtdsize, mtderasesize;
125 int matches;
126 char mtdname[64];
127 mtdname[0] = '\0';
128 mtdnum = -1;
129
130 matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]",
131 &mtdnum, &mtdsize, &mtderasesize, mtdname);
132 /* This will fail on the first line, which just contains
133 * column headers.
134 */
135 if (matches == 4) {
136 MtdPartition *p = &g_mtd_state.partitions[mtdnum];
137 p->device_index = mtdnum;
138 p->size = mtdsize;
139 p->erase_size = mtderasesize;
140 p->name = strdup(mtdname);
141 if (p->name == NULL) {
142 errno = ENOMEM;
143 goto bail;
144 }
145 g_mtd_state.partition_count++;
146 }
147
148 /* Eat the line.
149 */
150 while (nbytes > 0 && *bufp != '\n') {
151 bufp++;
152 nbytes--;
153 }
154 if (nbytes > 0) {
155 bufp++;
156 nbytes--;
157 }
158 }
159
160 return g_mtd_state.partition_count;
161
162bail:
163 // keep "partitions" around so we can free the names on a rescan.
164 g_mtd_state.partition_count = -1;
165 return -1;
166}
167
168const MtdPartition *
169mtd_find_partition_by_name(const char *name)
170{
171 if (g_mtd_state.partitions != NULL) {
172 int i;
173 for (i = 0; i < g_mtd_state.partitions_allocd; i++) {
174 MtdPartition *p = &g_mtd_state.partitions[i];
175 if (p->device_index >= 0 && p->name != NULL) {
176 if (strcmp(p->name, name) == 0) {
177 return p;
178 }
179 }
180 }
181 }
182 return NULL;
183}
184
185int
186mtd_mount_partition(const MtdPartition *partition, const char *mount_point,
187 const char *filesystem, int read_only)
188{
189 const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
190 char devname[64];
191 int rv = -1;
192
193 sprintf(devname, "/dev/block/mtdblock%d", partition->device_index);
194 if (!read_only) {
195 rv = mount(devname, mount_point, filesystem, flags, NULL);
196 }
197 if (read_only || rv < 0) {
198 rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0);
199 if (rv < 0) {
200 printf("Failed to mount %s on %s: %s\n",
201 devname, mount_point, strerror(errno));
202 } else {
203 printf("Mount %s on %s read-only\n", devname, mount_point);
204 }
205 }
206#if 1 //TODO: figure out why this is happening; remove include of stat.h
207 if (rv >= 0) {
208 /* For some reason, the x bits sometimes aren't set on the root
209 * of mounted volumes.
210 */
211 struct stat st;
212 rv = stat(mount_point, &st);
213 if (rv < 0) {
214 return rv;
215 }
216 mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
217 if (new_mode != st.st_mode) {
218printf("Fixing execute permissions for %s\n", mount_point);
219 rv = chmod(mount_point, new_mode);
220 if (rv < 0) {
221 printf("Couldn't fix permissions for %s: %s\n",
222 mount_point, strerror(errno));
223 }
224 }
225 }
226#endif
227 return rv;
228}
229
230int
231mtd_partition_info(const MtdPartition *partition,
232 size_t *total_size, size_t *erase_size, size_t *write_size)
233{
234 char mtddevname[32];
235 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
236 int fd = open(mtddevname, O_RDONLY);
237 if (fd < 0) return -1;
238
239 struct mtd_info_user mtd_info;
240 int ret = ioctl(fd, MEMGETINFO, &mtd_info);
241 close(fd);
242 if (ret < 0) return -1;
243
244 if (total_size != NULL) *total_size = mtd_info.size;
245 if (erase_size != NULL) *erase_size = mtd_info.erasesize;
246 if (write_size != NULL) *write_size = mtd_info.writesize;
247 return 0;
248}
249
250MtdReadContext *mtd_read_partition(const MtdPartition *partition)
251{
252 MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext));
253 if (ctx == NULL) return NULL;
254
255 ctx->buffer = malloc(partition->erase_size);
256 if (ctx->buffer == NULL) {
257 free(ctx);
258 return NULL;
259 }
260
261 char mtddevname[32];
262 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
263 ctx->fd = open(mtddevname, O_RDONLY);
264 if (ctx->fd < 0) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800265 free(ctx->buffer);
Christian Lindeberg862c83b2011-01-19 12:22:41 +0100266 free(ctx);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800267 return NULL;
268 }
269
270 ctx->partition = partition;
271 ctx->consumed = partition->erase_size;
272 return ctx;
273}
274
Doug Zongkerd12560a2010-09-14 14:25:48 -0700275// Seeks to a location in the partition. Don't mix with reads of
276// anything other than whole blocks; unpredictable things will result.
277void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) {
278 lseek64(ctx->fd, offset, SEEK_SET);
279}
280
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800281static int read_block(const MtdPartition *partition, int fd, char *data)
282{
283 struct mtd_ecc_stats before, after;
284 if (ioctl(fd, ECCGETSTATS, &before)) {
285 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
286 return -1;
287 }
288
Doug Zongker17a47092009-12-14 18:03:27 -0800289 loff_t pos = lseek64(fd, 0, SEEK_CUR);
290
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800291 ssize_t size = partition->erase_size;
Doug Zongker17a47092009-12-14 18:03:27 -0800292 int mgbb;
293
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800294 while (pos + size <= (int) partition->size) {
Doug Zongker17a47092009-12-14 18:03:27 -0800295 if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
296 fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800297 pos, strerror(errno));
298 } else if (ioctl(fd, ECCGETSTATS, &after)) {
299 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
300 return -1;
301 } else if (after.failed != before.failed) {
Doug Zongker17a47092009-12-14 18:03:27 -0800302 fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800303 after.corrected - before.corrected,
304 after.failed - before.failed, pos);
Doug Zongker5d6309e2010-11-03 14:31:01 -0700305 // copy the comparison baseline for the next read.
306 memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
Doug Zongker17a47092009-12-14 18:03:27 -0800307 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
308 fprintf(stderr,
309 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
310 mgbb, pos, errno);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800311 } else {
Doug Zongker61ba7a82010-09-12 13:36:40 -0700312 return 0; // Success!
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800313 }
314
315 pos += partition->erase_size;
316 }
317
318 errno = ENOSPC;
319 return -1;
320}
321
322ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
323{
Dees_Troy51a0e822012-09-05 15:24:24 -0400324 ssize_t read = 0;
325 while (read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800326 if (ctx->consumed < ctx->partition->erase_size) {
327 size_t avail = ctx->partition->erase_size - ctx->consumed;
328 size_t copy = len - read < avail ? len - read : avail;
329 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
330 ctx->consumed += copy;
331 read += copy;
332 }
333
334 // Read complete blocks directly into the user's buffer
335 while (ctx->consumed == ctx->partition->erase_size &&
336 len - read >= ctx->partition->erase_size) {
337 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
338 read += ctx->partition->erase_size;
339 }
340
Dees_Troy51a0e822012-09-05 15:24:24 -0400341 if (read >= (int)len) {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700342 return read;
343 }
344
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800345 // Read the next block into the buffer
Dees_Troy51a0e822012-09-05 15:24:24 -0400346 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800347 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
348 ctx->consumed = 0;
349 }
350 }
351
352 return read;
353}
354
355void mtd_read_close(MtdReadContext *ctx)
356{
357 close(ctx->fd);
358 free(ctx->buffer);
359 free(ctx);
360}
361
362MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
363{
364 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
365 if (ctx == NULL) return NULL;
366
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800367 ctx->bad_block_offsets = NULL;
368 ctx->bad_block_alloc = 0;
369 ctx->bad_block_count = 0;
370
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800371 ctx->buffer = malloc(partition->erase_size);
372 if (ctx->buffer == NULL) {
373 free(ctx);
374 return NULL;
375 }
376
377 char mtddevname[32];
378 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
379 ctx->fd = open(mtddevname, O_RDWR);
380 if (ctx->fd < 0) {
381 free(ctx->buffer);
382 free(ctx);
383 return NULL;
384 }
385
386 ctx->partition = partition;
387 ctx->stored = 0;
388 return ctx;
389}
390
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800391static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
392 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
393 ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
394 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
395 ctx->bad_block_alloc * sizeof(off_t));
396 }
397 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
398}
399
400static int write_block(MtdWriteContext *ctx, const char *data)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800401{
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800402 const MtdPartition *partition = ctx->partition;
403 int fd = ctx->fd;
404
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800405 off_t pos = lseek(fd, 0, SEEK_CUR);
406 if (pos == (off_t) -1) return 1;
407
408 ssize_t size = partition->erase_size;
409 while (pos + size <= (int) partition->size) {
410 loff_t bpos = pos;
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700411 int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
412 if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800413 add_bad_block_offset(ctx, pos);
Doug Zongkeraaf3f562010-09-09 16:41:49 -0700414 fprintf(stderr,
415 "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n",
416 pos, ret, errno);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800417 pos += partition->erase_size;
418 continue; // Don't try to erase known factory-bad blocks.
419 }
420
421 struct erase_info_user erase_info;
422 erase_info.start = pos;
423 erase_info.length = size;
424 int retry;
425 for (retry = 0; retry < 2; ++retry) {
426 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
427 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
428 pos, strerror(errno));
429 continue;
430 }
431 if (lseek(fd, pos, SEEK_SET) != pos ||
432 write(fd, data, size) != size) {
433 fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
434 pos, strerror(errno));
435 }
436
437 char verify[size];
438 if (lseek(fd, pos, SEEK_SET) != pos ||
439 read(fd, verify, size) != size) {
440 fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
441 pos, strerror(errno));
442 continue;
443 }
444 if (memcmp(data, verify, size) != 0) {
445 fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
446 pos, strerror(errno));
447 continue;
448 }
449
450 if (retry > 0) {
451 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
452 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400453 fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800454 return 0; // Success!
455 }
456
457 // Try to erase it once more as we give up on this block
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800458 add_bad_block_offset(ctx, pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800459 fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
460 ioctl(fd, MEMERASE, &erase_info);
461 pos += partition->erase_size;
462 }
463
464 // Ran out of space on the device
465 errno = ENOSPC;
466 return -1;
467}
468
469ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
470{
471 size_t wrote = 0;
472 while (wrote < len) {
473 // Coalesce partial writes into complete blocks
474 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
475 size_t avail = ctx->partition->erase_size - ctx->stored;
476 size_t copy = len - wrote < avail ? len - wrote : avail;
477 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
478 ctx->stored += copy;
479 wrote += copy;
480 }
481
482 // If a complete block was accumulated, write it
483 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800484 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800485 ctx->stored = 0;
486 }
487
488 // Write complete blocks directly from the user's buffer
489 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800490 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800491 wrote += ctx->partition->erase_size;
492 }
493 }
494
495 return wrote;
496}
497
498off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
499{
500 // Zero-pad and write any pending data to get us to a block boundary
501 if (ctx->stored > 0) {
502 size_t zero = ctx->partition->erase_size - ctx->stored;
503 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800504 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800505 ctx->stored = 0;
506 }
507
508 off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
509 if ((off_t) pos == (off_t) -1) return pos;
510
511 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
512 if (blocks < 0) blocks = total;
513 if (blocks > total) {
514 errno = ENOSPC;
515 return -1;
516 }
517
518 // Erase the specified number of blocks
519 while (blocks-- > 0) {
520 loff_t bpos = pos;
521 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
522 fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos);
523 pos += ctx->partition->erase_size;
524 continue; // Don't try to erase known factory-bad blocks.
525 }
526
527 struct erase_info_user erase_info;
528 erase_info.start = pos;
529 erase_info.length = ctx->partition->erase_size;
530 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
531 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
532 }
533 pos += ctx->partition->erase_size;
534 }
535
536 return pos;
537}
538
539int mtd_write_close(MtdWriteContext *ctx)
540{
541 int r = 0;
542 // Make sure any pending data gets written
543 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
544 if (close(ctx->fd)) r = -1;
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800545 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800546 free(ctx->buffer);
547 free(ctx);
548 return r;
549}
Doug Zongker4c5f9f32010-01-12 16:18:33 -0800550
551/* Return the offset of the first good block at or after pos (which
552 * might be pos itself).
553 */
554off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
555 int i;
556 for (i = 0; i < ctx->bad_block_count; ++i) {
557 if (ctx->bad_block_offsets[i] == pos) {
558 pos += ctx->partition->erase_size;
559 } else if (ctx->bad_block_offsets[i] > pos) {
560 return pos;
561 }
562 }
563 return pos;
564}
Dees_Troy51a0e822012-09-05 15:24:24 -0400565
566#define BLOCK_SIZE 2048
567#define SPARE_SIZE (BLOCK_SIZE >> 5)
568#define HEADER_SIZE 2048
569
570int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
571{
572 const MtdPartition *ptn;
573 MtdWriteContext *write;
574 void *data;
575
576 FILE* f = fopen(filename, "rb");
577 if (f == NULL) {
578 fprintf(stderr, "error opening %s", filename);
579 return -1;
580 }
581
582 if (mtd_scan_partitions() <= 0)
583 {
584 fprintf(stderr, "error scanning partitions");
585 return -1;
586 }
587 const MtdPartition *mtd = mtd_find_partition_by_name(partition_name);
588 if (mtd == NULL)
589 {
590 fprintf(stderr, "can't find %s partition", partition_name);
591 return -1;
592 }
593
594 int fd = open(filename, O_RDONLY);
595 if (fd < 0)
596 {
597 printf("error opening %s", filename);
598 return -1;
599 }
600
601 MtdWriteContext* ctx = mtd_write_partition(mtd);
602 if (ctx == NULL) {
603 printf("error writing %s", partition_name);
604 return -1;
605 }
606
607 int success = 1;
608 char* buffer = malloc(BUFSIZ);
609 int read;
610 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
611 int wrote = mtd_write_data(ctx, buffer, read);
612 success = success && (wrote == read);
613 }
614 free(buffer);
615 fclose(f);
616
617 if (!success) {
618 fprintf(stderr, "error writing %s", partition_name);
619 return -1;
620 }
621
622 if (mtd_erase_blocks(ctx, -1) == -1) {
623 fprintf(stderr, "error erasing blocks of %s\n", partition_name);
624 }
625 if (mtd_write_close(ctx) != 0) {
626 fprintf(stderr, "error closing write of %s\n", partition_name);
627 }
628 printf("%s %s partition\n", success ? "wrote" : "failed to write", partition_name);
629 return 0;
630}
631
632
633int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
634{
635 MtdReadContext *in;
636 const MtdPartition *partition;
637 char buf[BLOCK_SIZE + SPARE_SIZE];
638 size_t partition_size;
639 size_t read_size;
640 size_t total;
641 int fd;
642 int wrote;
643 int len;
644
645 if (mtd_scan_partitions() <= 0)
646 {
647 printf("error scanning partitions");
648 return -1;
649 }
650
651 partition = mtd_find_partition_by_name(partition_name);
652 if (partition == NULL)
653 {
654 printf("can't find %s partition", partition_name);
655 return -1;
656 }
657
658 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
659 printf("can't get info of partition %s", partition_name);
660 return -1;
661 }
662
663 if (!strcmp(filename, "-")) {
664 fd = fileno(stdout);
665 }
666 else {
667 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
668 }
669
670 if (fd < 0)
671 {
672 printf("error opening %s", filename);
673 return -1;
674 }
675
676 in = mtd_read_partition(partition);
677 if (in == NULL) {
678 close(fd);
679 unlink(filename);
680 printf("error opening %s: %s\n", partition_name, strerror(errno));
681 return -1;
682 }
683
684 total = 0;
685 while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
686 wrote = write(fd, buf, len);
687 if (wrote != len) {
688 close(fd);
689 unlink(filename);
690 printf("error writing %s", filename);
691 return -1;
692 }
693 total += BLOCK_SIZE;
694 }
695
696 mtd_read_close(in);
697
698 if (close(fd)) {
699 unlink(filename);
700 printf("error closing %s", filename);
701 return -1;
702 }
703 return 0;
704}
705
706int cmd_mtd_erase_raw_partition(const char *partition_name)
707{
708 MtdWriteContext *out;
709 size_t erased;
710 size_t total_size;
711 size_t erase_size;
712
713 if (mtd_scan_partitions() <= 0)
714 {
715 printf("error scanning partitions");
716 return -1;
717 }
718 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
719 if (p == NULL)
720 {
721 printf("can't find %s partition", partition_name);
722 return -1;
723 }
724
725 out = mtd_write_partition(p);
726 if (out == NULL)
727 {
728 printf("could not estabilish write context for %s", partition_name);
729 return -1;
730 }
731
732 // do the actual erase, -1 = full partition erase
733 erased = mtd_erase_blocks(out, -1);
734
735 // erased = bytes erased, if zero, something borked
736 if (!erased)
737 {
738 printf("error erasing %s", partition_name);
739 return -1;
740 }
741
742 return 0;
743}
744
745int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
746{
747 return cmd_mtd_erase_raw_partition(partition);
748}
749
750
751int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
752{
753 mtd_scan_partitions();
754 const MtdPartition *p;
755 p = mtd_find_partition_by_name(partition);
756 if (p == NULL) {
757 return -1;
758 }
759 return mtd_mount_partition(p, mount_point, filesystem, read_only);
760}
761
762int cmd_mtd_get_partition_device(const char *partition, char *device)
763{
764 mtd_scan_partitions();
765 MtdPartition *p = mtd_find_partition_by_name(partition);
766 if (p == NULL)
767 return -1;
768 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
769 return 0;
770}