]> www.infradead.org Git - mtd-utils.git/commitdiff
nandwrite: unified reading from standard input and from file - part 3
authorJehan Bing <jehan@orb.com>
Thu, 6 Aug 2009 00:40:55 +0000 (17:40 -0700)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tue, 11 Aug 2009 13:14:20 +0000 (16:14 +0300)
Nandwrite tries to use lseek() when failing to write on a page. lseek()
will fail when used on the standard input so nandwrite fails. This code
replaces lseek with a buffer.

When the data is read, it is put in a buffer (filebuf). This buffer is
reset at each block boundary. So a "seek" just means reading from the
beginning of the buffer. writebuf and oobreadbuf are now just pointers
to locations in filebuf.

Signed-off-by: Jehan Bing <jehan@orb.com>
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
nandwrite.c

index f6bff60b51b3306b1e066af570332a1127cacffe..d3486b9339d27af9cdbf533632e7eecbcc3eba88 100644 (file)
 #define MAX_PAGE_SIZE  4096
 #define MAX_OOB_SIZE   128
 
-/*
- * Buffer array used for writing data
- */
-static unsigned char writebuf[MAX_PAGE_SIZE];
-static unsigned char oobbuf[MAX_OOB_SIZE];
-static unsigned char oobreadbuf[MAX_OOB_SIZE];
-
 // oob layouts to pass into the kernel as default
 static struct nand_oobinfo none_oobinfo = {
        .useecc = MTD_NANDECC_OFF,
@@ -257,10 +250,19 @@ int main(int argc, char * const argv[])
        struct mtd_info_user meminfo;
        struct mtd_oob_buf oob;
        loff_t offs;
-       int ret, readlen;
+       int ret;
        int oobinfochanged = 0;
        struct nand_oobinfo old_oobinfo;
        bool failed = true;
+       // contains all the data read from the file so far for the current eraseblock
+       unsigned char *filebuf = NULL;
+       size_t filebuf_max = 0;
+       size_t filebuf_len = 0;
+       // points to the current page inside filebuf
+       unsigned char *writebuf = NULL;
+       // points to the OOB for the current page in filebuf
+       unsigned char *oobreadbuf = NULL;
+       unsigned char oobbuf[MAX_OOB_SIZE];
 
        process_options(argc, argv);
 
@@ -431,6 +433,16 @@ int main(int argc, char * const argv[])
                goto closeall;
        }
 
+       // Allocate a buffer big enough to contain all the data (OOB included) for one eraseblock
+       filebuf_max = pagelen * meminfo.erasesize / meminfo.writesize;
+       filebuf = (unsigned char*)malloc(filebuf_max);
+       if (!filebuf) {
+               fprintf(stderr, "Failed to allocate memory for file buffer (%d bytes)\n",
+                               pagelen * meminfo.erasesize / meminfo.writesize);
+               goto closeall;
+       }
+       erase_buffer(filebuf, filebuf_max);
+
        /*
         * Get data from input and write to the device while there is
         * still input to read and we are still within the device
@@ -438,7 +450,9 @@ int main(int argc, char * const argv[])
         * length is simply a quasi-boolean flag whose values are page
         * length or zero.
         */
-       while (imglen && (mtdoffset < meminfo.size)) {
+       while (((imglen > 0) || (writebuf < (filebuf + filebuf_len)))
+               && (mtdoffset < meminfo.size))
+       {
                // new eraseblock , check for bad block(s)
                // Stay in the loop to be sure if the mtdoffset changes because
                // of a bad block, that the next block that will be written to
@@ -448,6 +462,15 @@ int main(int argc, char * const argv[])
                while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
                        blockstart = mtdoffset & (~meminfo.erasesize + 1);
                        offs = blockstart;
+
+                       // if writebuf == filebuf, we are rewinding so we must not
+                       // reset the buffer but just replay it
+                       if (writebuf != filebuf) {
+                               erase_buffer(filebuf, filebuf_len);
+                               filebuf_len = 0;
+                               writebuf = filebuf;
+                       }
+
                        baderaseblock = false;
                        if (!quiet)
                                fprintf (stdout, "Writing data to block %d at offset 0x%x\n",
@@ -475,10 +498,12 @@ int main(int argc, char * const argv[])
 
                }
 
-               {
-                       readlen = meminfo.writesize;
+               // Read more data from the input if there isn't enough in the buffer
+               if ((writebuf + meminfo.writesize) > (filebuf + filebuf_len)) {
+                       int readlen = meminfo.writesize;
 
-                       int tinycnt = 0;
+                       int alreadyread = (filebuf + filebuf_len) - writebuf;
+                       int tinycnt = alreadyread;
 
                        while (tinycnt < readlen) {
                                cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
@@ -513,17 +538,25 @@ int main(int argc, char * const argv[])
                                erase_buffer(writebuf + tinycnt, readlen - tinycnt);
                        }
 
-                       if ((ifd == STDIN_FILENO) && (cnt == 0)) {
+                       filebuf_len += readlen - alreadyread;
+                       if (ifd != STDIN_FILENO) {
+                               imglen -= tinycnt - alreadyread;
+                       }
+                       else if (cnt == 0) {
                                /* No more bytes - we are done after writing the remaining bytes */
                                imglen = 0;
                        }
                }
 
                if (writeoob) {
-                       {
+                       oobreadbuf = writebuf + meminfo.writesize;
+
+                       // Read more data for the OOB from the input if there isn't enough in the buffer
+                       if ((oobreadbuf + meminfo.oobsize) > (filebuf + filebuf_len)) {
                                int readlen = meminfo.oobsize;
 
-                               int tinycnt = 0;
+                               int alreadyread = (filebuf + filebuf_len) - oobreadbuf;
+                               int tinycnt = alreadyread;
 
                                while (tinycnt < readlen) {
                                        cnt = read(ifd, oobreadbuf + tinycnt, readlen - tinycnt);
@@ -542,13 +575,19 @@ int main(int argc, char * const argv[])
                                        goto closeall;
                                }
 
-                               if ((ifd == STDIN_FILENO) && (cnt == 0)) {
+                               filebuf_len += readlen - alreadyread;
+                               if (ifd != STDIN_FILENO) {
+                                       imglen -= tinycnt - alreadyread;
+                               }
+                               else if (cnt == 0) {
                                        /* No more bytes - we are done after writing the remaining bytes */
                                        imglen = 0;
                                }
                        }
 
-                       if (!noecc) {
+                       if (noecc) {
+                               oob.ptr = oobreadbuf;
+                       } else {
                                int i, start, len;
                                /*
                                 *  We use autoplacement and have the oobinfo with the autoplacement
@@ -581,13 +620,10 @@ int main(int argc, char * const argv[])
                                perror ("ioctl(MEMWRITEOOB)");
                                goto closeall;
                        }
-                       imglen -= meminfo.oobsize;
                }
 
                /* Write out the Page data */
                if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
-                       int rewind_blocks;
-                       off_t rewind_bytes;
                        erase_info_t erase;
 
                        perror ("pwrite");
@@ -596,15 +632,8 @@ int main(int argc, char * const argv[])
                        }
 
                        /* Must rewind to blockstart if we can */
-                       rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
-                       rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
-                       if (writeoob)
-                               rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize;
-                       if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
-                               perror("lseek");
-                               fprintf(stderr, "Failed to seek backwards to recover from write error\n");
-                               goto closeall;
-                       }
+                       writebuf = filebuf;
+
                        erase.start = blockstart;
                        erase.length = meminfo.erasesize;
                        fprintf(stderr, "Erasing failed write from %08lx-%08lx\n",
@@ -625,19 +654,20 @@ int main(int argc, char * const argv[])
                                }
                        }
                        mtdoffset = blockstart + meminfo.erasesize;
-                       imglen += rewind_blocks * meminfo.writesize;
 
                        continue;
                }
-               if (ifd != STDIN_FILENO) {
-                       imglen -= readlen;
-               }
                mtdoffset += meminfo.writesize;
+               writebuf += pagelen;
        }
 
        failed = false;
 
 closeall:
+       if (filebuf) {
+               free(filebuf);
+       }
+
        close(ifd);
 
 restoreoob:
@@ -651,7 +681,10 @@ restoreoob:
 
        close(fd);
 
-       if (failed || ((ifd != STDIN_FILENO) && (imglen > 0))) {
+       if (failed
+               || ((ifd != STDIN_FILENO) && (imglen > 0))
+               || (writebuf < (filebuf + filebuf_len)))
+       {
                perror ("Data was only partially written due to error\n");
                exit (EXIT_FAILURE);
        }