blob: 9fd5c6c1f1bf21281b8fedba7d7fdea0962fbc18 [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 Zongker22d79a52010-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) {
265 free(ctx);
266 free(ctx->buffer);
267 return NULL;
268 }
269
270 ctx->partition = partition;
271 ctx->consumed = partition->erase_size;
272 return ctx;
273}
274
275static int read_block(const MtdPartition *partition, int fd, char *data)
276{
277 struct mtd_ecc_stats before, after;
278 if (ioctl(fd, ECCGETSTATS, &before)) {
279 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
280 return -1;
281 }
282
Doug Zongker17a47092009-12-14 18:03:27 -0800283 loff_t pos = lseek64(fd, 0, SEEK_CUR);
284
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800285 ssize_t size = partition->erase_size;
Doug Zongker17a47092009-12-14 18:03:27 -0800286 int mgbb;
287
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800288 while (pos + size <= (int) partition->size) {
Doug Zongker17a47092009-12-14 18:03:27 -0800289 if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
290 fprintf(stderr, "mtd: read error at 0x%08llx (%s)\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800291 pos, strerror(errno));
292 } else if (ioctl(fd, ECCGETSTATS, &after)) {
293 fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno));
294 return -1;
295 } else if (after.failed != before.failed) {
Doug Zongker17a47092009-12-14 18:03:27 -0800296 fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800297 after.corrected - before.corrected,
298 after.failed - before.failed, pos);
Doug Zongker17a47092009-12-14 18:03:27 -0800299 } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
300 fprintf(stderr,
301 "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
302 mgbb, pos, errno);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800303 } else {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700304 int i;
305 for (i = 0; i < size; ++i) {
306 if (data[i] != 0) {
307 return 0; // Success!
308 }
309 }
Doug Zongker17a47092009-12-14 18:03:27 -0800310 fprintf(stderr, "mtd: read all-zero block at 0x%08llx; skipping\n",
Doug Zongkerbec02d52009-07-01 12:09:29 -0700311 pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800312 }
313
314 pos += partition->erase_size;
315 }
316
317 errno = ENOSPC;
318 return -1;
319}
320
321ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len)
322{
323 ssize_t read = 0;
324 while (read < (int) len) {
325 if (ctx->consumed < ctx->partition->erase_size) {
326 size_t avail = ctx->partition->erase_size - ctx->consumed;
327 size_t copy = len - read < avail ? len - read : avail;
328 memcpy(data + read, ctx->buffer + ctx->consumed, copy);
329 ctx->consumed += copy;
330 read += copy;
331 }
332
333 // Read complete blocks directly into the user's buffer
334 while (ctx->consumed == ctx->partition->erase_size &&
335 len - read >= ctx->partition->erase_size) {
336 if (read_block(ctx->partition, ctx->fd, data + read)) return -1;
337 read += ctx->partition->erase_size;
338 }
339
Antonio Vazquez158d25c2010-11-13 04:51:11 +0800340 if (read >= (int)len) {
Doug Zongkerbec02d52009-07-01 12:09:29 -0700341 return read;
342 }
343
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800344 // Read the next block into the buffer
345 if (ctx->consumed == ctx->partition->erase_size && read < (int) len) {
346 if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
347 ctx->consumed = 0;
348 }
349 }
350
351 return read;
352}
353
354void mtd_read_close(MtdReadContext *ctx)
355{
356 close(ctx->fd);
357 free(ctx->buffer);
358 free(ctx);
359}
360
361MtdWriteContext *mtd_write_partition(const MtdPartition *partition)
362{
363 MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
364 if (ctx == NULL) return NULL;
365
Doug Zongker22d79a52010-01-12 16:18:33 -0800366 ctx->bad_block_offsets = NULL;
367 ctx->bad_block_alloc = 0;
368 ctx->bad_block_count = 0;
369
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800370 ctx->buffer = malloc(partition->erase_size);
371 if (ctx->buffer == NULL) {
372 free(ctx);
373 return NULL;
374 }
375
376 char mtddevname[32];
377 sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
378 ctx->fd = open(mtddevname, O_RDWR);
379 if (ctx->fd < 0) {
380 free(ctx->buffer);
381 free(ctx);
382 return NULL;
383 }
384
385 ctx->partition = partition;
386 ctx->stored = 0;
387 return ctx;
388}
389
Doug Zongker22d79a52010-01-12 16:18:33 -0800390static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
391 if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
392 ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
393 ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
394 ctx->bad_block_alloc * sizeof(off_t));
395 }
396 ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
397}
398
399static int write_block(MtdWriteContext *ctx, const char *data)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800400{
Doug Zongker22d79a52010-01-12 16:18:33 -0800401 const MtdPartition *partition = ctx->partition;
402 int fd = ctx->fd;
403
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800404 off_t pos = lseek(fd, 0, SEEK_CUR);
405 if (pos == (off_t) -1) return 1;
406
407 ssize_t size = partition->erase_size;
408 while (pos + size <= (int) partition->size) {
409 loff_t bpos = pos;
410 if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) {
Doug Zongker22d79a52010-01-12 16:18:33 -0800411 add_bad_block_offset(ctx, pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800412 fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos);
413 pos += partition->erase_size;
414 continue; // Don't try to erase known factory-bad blocks.
415 }
416
417 struct erase_info_user erase_info;
418 erase_info.start = pos;
419 erase_info.length = size;
420 int retry;
421 for (retry = 0; retry < 2; ++retry) {
422 if (ioctl(fd, MEMERASE, &erase_info) < 0) {
423 fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
424 pos, strerror(errno));
425 continue;
426 }
427 if (lseek(fd, pos, SEEK_SET) != pos ||
428 write(fd, data, size) != size) {
429 fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
430 pos, strerror(errno));
431 }
432
433 char verify[size];
434 if (lseek(fd, pos, SEEK_SET) != pos ||
435 read(fd, verify, size) != size) {
436 fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
437 pos, strerror(errno));
438 continue;
439 }
440 if (memcmp(data, verify, size) != 0) {
441 fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
442 pos, strerror(errno));
443 continue;
444 }
445
446 if (retry > 0) {
447 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
448 }
449 return 0; // Success!
450 }
451
452 // Try to erase it once more as we give up on this block
Doug Zongker22d79a52010-01-12 16:18:33 -0800453 add_bad_block_offset(ctx, pos);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800454 fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
455 ioctl(fd, MEMERASE, &erase_info);
456 pos += partition->erase_size;
457 }
458
459 // Ran out of space on the device
460 errno = ENOSPC;
461 return -1;
462}
463
464ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len)
465{
466 size_t wrote = 0;
467 while (wrote < len) {
468 // Coalesce partial writes into complete blocks
469 if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) {
470 size_t avail = ctx->partition->erase_size - ctx->stored;
471 size_t copy = len - wrote < avail ? len - wrote : avail;
472 memcpy(ctx->buffer + ctx->stored, data + wrote, copy);
473 ctx->stored += copy;
474 wrote += copy;
475 }
476
477 // If a complete block was accumulated, write it
478 if (ctx->stored == ctx->partition->erase_size) {
Doug Zongker22d79a52010-01-12 16:18:33 -0800479 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800480 ctx->stored = 0;
481 }
482
483 // Write complete blocks directly from the user's buffer
484 while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
Doug Zongker22d79a52010-01-12 16:18:33 -0800485 if (write_block(ctx, data + wrote)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800486 wrote += ctx->partition->erase_size;
487 }
488 }
489
490 return wrote;
491}
492
493off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks)
494{
495 // Zero-pad and write any pending data to get us to a block boundary
496 if (ctx->stored > 0) {
497 size_t zero = ctx->partition->erase_size - ctx->stored;
498 memset(ctx->buffer + ctx->stored, 0, zero);
Doug Zongker22d79a52010-01-12 16:18:33 -0800499 if (write_block(ctx, ctx->buffer)) return -1;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800500 ctx->stored = 0;
501 }
502
503 off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
504 if ((off_t) pos == (off_t) -1) return pos;
505
506 const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
507 if (blocks < 0) blocks = total;
508 if (blocks > total) {
509 errno = ENOSPC;
510 return -1;
511 }
512
513 // Erase the specified number of blocks
514 while (blocks-- > 0) {
515 loff_t bpos = pos;
516 if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) {
517 fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos);
518 pos += ctx->partition->erase_size;
519 continue; // Don't try to erase known factory-bad blocks.
520 }
521
522 struct erase_info_user erase_info;
523 erase_info.start = pos;
524 erase_info.length = ctx->partition->erase_size;
525 if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) {
526 fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos);
527 }
528 pos += ctx->partition->erase_size;
529 }
530
531 return pos;
532}
533
534int mtd_write_close(MtdWriteContext *ctx)
535{
536 int r = 0;
537 // Make sure any pending data gets written
538 if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
539 if (close(ctx->fd)) r = -1;
Doug Zongker22d79a52010-01-12 16:18:33 -0800540 free(ctx->bad_block_offsets);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800541 free(ctx->buffer);
542 free(ctx);
543 return r;
544}
Doug Zongker22d79a52010-01-12 16:18:33 -0800545
546/* Return the offset of the first good block at or after pos (which
547 * might be pos itself).
548 */
549off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
550 int i;
551 for (i = 0; i < ctx->bad_block_count; ++i) {
552 if (ctx->bad_block_offsets[i] == pos) {
553 pos += ctx->partition->erase_size;
554 } else if (ctx->bad_block_offsets[i] > pos) {
555 return pos;
556 }
557 }
558 return pos;
559}
Koushik Dutta19447c02010-11-10 10:40:44 -0800560
561#define BLOCK_SIZE 2048
562#define SPARE_SIZE (BLOCK_SIZE >> 5)
563#define HEADER_SIZE 2048
564
Steve Kondik4123b582010-11-14 03:18:40 -0500565int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
Koushik Dutta19447c02010-11-10 10:40:44 -0800566{
567 const MtdPartition *ptn;
568 MtdWriteContext *write;
569 void *data;
570 unsigned sz;
571
572 if (mtd_scan_partitions() <= 0)
573 {
574 printf("error scanning partitions");
575 return -1;
576 }
577 const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
578 if (partition == NULL)
579 {
Steve Kondik4123b582010-11-14 03:18:40 -0500580 printf("can't find %s partition", partition_name);
Koushik Dutta19447c02010-11-10 10:40:44 -0800581 return -1;
Steve Kondik4123b582010-11-14 03:18:40 -0500582 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800583
584 // If the first part of the file matches the partition, skip writing
585
586 int fd = open(filename, O_RDONLY);
587 if (fd < 0)
588 {
589 printf("error opening %s", filename);
590 return -1;
Steve Kondik4123b582010-11-14 03:18:40 -0500591 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800592
593 char header[HEADER_SIZE];
594 int headerlen = read(fd, header, sizeof(header));
Steve Kondik4123b582010-11-14 03:18:40 -0500595 if (headerlen <= 0)
Koushik Dutta19447c02010-11-10 10:40:44 -0800596 {
597 printf("error reading %s header", filename);
598 return -1;
599 }
600
601 MtdReadContext *in = mtd_read_partition(partition);
602 if (in == NULL) {
603 printf("error opening %s: %s\n", partition, strerror(errno));
604 // just assume it needs re-writing
605 } else {
606 char check[HEADER_SIZE];
607 int checklen = mtd_read_data(in, check, sizeof(check));
608 if (checklen <= 0) {
609 printf("error reading %s: %s\n", partition_name, strerror(errno));
610 // just assume it needs re-writing
611 } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
612 printf("header is the same, not flashing %s\n", partition_name);
613 return 0;
614 }
615 mtd_read_close(in);
616 }
617
618 // Skip the header (we'll come back to it), write everything else
619 printf("flashing %s from %s\n", partition_name, filename);
620
621 MtdWriteContext *out = mtd_write_partition(partition);
Steve Kondik4123b582010-11-14 03:18:40 -0500622 if (out == NULL)
Koushik Dutta19447c02010-11-10 10:40:44 -0800623 {
624 printf("error writing %s", partition_name);
625 return -1;
626 }
627
628 char buf[HEADER_SIZE];
629 memset(buf, 0, headerlen);
630 int wrote = mtd_write_data(out, buf, headerlen);
Steve Kondik4123b582010-11-14 03:18:40 -0500631 if (wrote != headerlen)
Koushik Dutta19447c02010-11-10 10:40:44 -0800632 {
633 printf("error writing %s", partition_name);
634 return -1;
635 }
636
637 int len;
638 while ((len = read(fd, buf, sizeof(buf))) > 0) {
639 wrote = mtd_write_data(out, buf, len);
640 if (wrote != len)
641 {
642 printf("error writing %s", partition_name);
643 return -1;
644 }
645 }
Steve Kondik4123b582010-11-14 03:18:40 -0500646 if (len < 0)
Koushik Dutta19447c02010-11-10 10:40:44 -0800647 {
648 printf("error reading %s", filename);
649 return -1;
650 }
651
Steve Kondik4123b582010-11-14 03:18:40 -0500652 if (mtd_write_close(out))
Koushik Dutta19447c02010-11-10 10:40:44 -0800653 {
654 printf("error closing %s", partition_name);
655 return -1;
656 }
657
658 // Now come back and write the header last
659
660 out = mtd_write_partition(partition);
661 if (out == NULL)
662 {
663 printf("error re-opening %s", partition_name);
664 return -1;
Steve Kondik4123b582010-11-14 03:18:40 -0500665 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800666
667 wrote = mtd_write_data(out, header, headerlen);
668 if (wrote != headerlen)
669 {
670 printf("error re-writing %s", partition_name);
671 return -1;
Steve Kondik4123b582010-11-14 03:18:40 -0500672 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800673
674 // Need to write a complete block, so write the rest of the first block
675 size_t block_size;
676 if (mtd_partition_info(partition, NULL, &block_size, NULL))
677 {
678 printf("error getting %s block size", partition_name);
679 return -1;
680 }
681
682 if (lseek(fd, headerlen, SEEK_SET) != headerlen)
683 {
684 printf("error rewinding %s", filename);
685 return -1;
686 }
687
688 int left = block_size - headerlen;
689 while (left < 0) left += block_size;
690 while (left > 0) {
691 len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
692 if (len <= 0){
693 printf("error reading %s", filename);
694 return -1;
695 }
696 if (mtd_write_data(out, buf, len) != len)
697 {
698 printf("error writing %s", partition_name);
699 return -1;
700 }
Steve Kondik4123b582010-11-14 03:18:40 -0500701
Koushik Dutta19447c02010-11-10 10:40:44 -0800702 left -= len;
703 }
704
Steve Kondik4123b582010-11-14 03:18:40 -0500705 if (mtd_write_close(out))
Koushik Dutta19447c02010-11-10 10:40:44 -0800706 {
707 printf("error closing %s", partition_name);
708 return -1;
709 }
710 return 0;
711}
712
713
Steve Kondik4123b582010-11-14 03:18:40 -0500714int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
Koushik Dutta19447c02010-11-10 10:40:44 -0800715{
716 MtdReadContext *in;
717 const MtdPartition *partition;
718 char buf[BLOCK_SIZE + SPARE_SIZE];
719 size_t partition_size;
720 size_t read_size;
721 size_t total;
722 int fd;
723 int wrote;
724 int len;
Steve Kondik4123b582010-11-14 03:18:40 -0500725
Koushik Dutta19447c02010-11-10 10:40:44 -0800726 if (mtd_scan_partitions() <= 0)
727 {
Steve Kondik4123b582010-11-14 03:18:40 -0500728 printf("error scanning partitions");
Koushik Dutta19447c02010-11-10 10:40:44 -0800729 return -1;
730 }
731
732 partition = mtd_find_partition_by_name(partition_name);
733 if (partition == NULL)
734 {
735 printf("can't find %s partition", partition_name);
736 return -1;
737 }
738
739 if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
740 printf("can't get info of partition %s", partition_name);
741 return -1;
742 }
743
744 if (!strcmp(filename, "-")) {
745 fd = fileno(stdout);
Steve Kondik4123b582010-11-14 03:18:40 -0500746 }
Koushik Dutta19447c02010-11-10 10:40:44 -0800747 else {
748 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
749 }
750
751 if (fd < 0)
752 {
753 printf("error opening %s", filename);
754 return -1;
755 }
756
757 in = mtd_read_partition(partition);
758 if (in == NULL) {
759 close(fd);
760 unlink(filename);
761 printf("error opening %s: %s\n", partition_name, strerror(errno));
762 return -1;
763 }
764
765 total = 0;
766 while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
767 wrote = write(fd, buf, len);
768 if (wrote != len) {
769 close(fd);
770 unlink(filename);
771 printf("error writing %s", filename);
772 return -1;
773 }
774 total += BLOCK_SIZE;
775 }
776
777 mtd_read_close(in);
778
779 if (close(fd)) {
780 unlink(filename);
781 printf("error closing %s", filename);
782 return -1;
783 }
784 return 0;
785}
786
Steve Kondik4123b582010-11-14 03:18:40 -0500787int cmd_mtd_erase_raw_partition(const char *partition_name)
Koushik Dutta19447c02010-11-10 10:40:44 -0800788{
789 MtdWriteContext *out;
790 size_t erased;
791 size_t total_size;
792 size_t erase_size;
793
794 if (mtd_scan_partitions() <= 0)
795 {
796 printf("error scanning partitions");
797 return -1;
798 }
799 const MtdPartition *p = mtd_find_partition_by_name(partition_name);
800 if (p == NULL)
801 {
Steve Kondik4123b582010-11-14 03:18:40 -0500802 printf("can't find %s partition", partition_name);
Koushik Dutta19447c02010-11-10 10:40:44 -0800803 return -1;
804 }
805
806 out = mtd_write_partition(p);
807 if (out == NULL)
808 {
Steve Kondik4123b582010-11-14 03:18:40 -0500809 printf("could not estabilish write context for %s", partition_name);
Koushik Dutta19447c02010-11-10 10:40:44 -0800810 return -1;
811 }
812
813 // do the actual erase, -1 = full partition erase
814 erased = mtd_erase_blocks(out, -1);
815
816 // erased = bytes erased, if zero, something borked
817 if (!erased)
818 {
819 printf("error erasing %s", partition_name);
820 return -1;
821 }
822
823 return 0;
824}
825
Steve Kondik4123b582010-11-14 03:18:40 -0500826int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
Koushik Dutta19447c02010-11-10 10:40:44 -0800827{
Steve Kondik4123b582010-11-14 03:18:40 -0500828 return cmd_mtd_erase_raw_partition(partition);
Koushik Dutta19447c02010-11-10 10:40:44 -0800829}
830
831
Steve Kondik4123b582010-11-14 03:18:40 -0500832int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
Koushik Dutta19447c02010-11-10 10:40:44 -0800833{
834 mtd_scan_partitions();
835 const MtdPartition *p;
836 p = mtd_find_partition_by_name(partition);
837 if (p == NULL) {
838 return -1;
839 }
840 return mtd_mount_partition(p, mount_point, filesystem, read_only);
841}
842
Steve Kondik4123b582010-11-14 03:18:40 -0500843int cmd_mtd_get_partition_device(const char *partition, char *device)
Koushik Dutta19447c02010-11-10 10:40:44 -0800844{
845 mtd_scan_partitions();
846 MtdPartition *p = mtd_find_partition_by_name(partition);
847 if (p == NULL)
848 return -1;
849 sprintf(device, "/dev/block/mtdblock%d", p->device_index);
850 return 0;
851}