]> git.sur5r.net Git - u-boot/commitdiff
fs/fat/fat_write: Fix buffer alignments
authorBenoît Thébaudeau <benoit@wsystem.com>
Mon, 28 Sep 2015 13:45:28 +0000 (15:45 +0200)
committerTom Rini <trini@konsulko.com>
Sun, 11 Oct 2015 21:12:07 +0000 (17:12 -0400)
set_cluster() was using a temporary buffer without enforcing its
alignment for DMA and cache. Moreover, it did not check the alignment of
the passed buffer, which can come directly from applicative code or from
the user.

This could cause random data corruption, which has been observed on
i.MX25 writing to an SD card.

Fix this by only passing ARCH_DMA_MINALIGN-aligned buffers to
disk_write(), which requires the introduction of a buffer bouncing
mechanism for the misaligned buffers passed to set_cluster().

By the way, improve the handling of the corresponding return values from
disk_write():
 - print them with debug() in case of error,
 - consider that there is an error is disk_write() returns a smaller
   block count than the requested one, not only if its return value is
   negative.

After this change, set_cluster() and get_cluster() are almost
symmetrical.

Signed-off-by: Benoît Thébaudeau <benoit@wsystem.com>
fs/fat/fat_write.c

index adb6940dffcc3f6f63843b4a5d70af3d4e66f991..d0d9df717093ed95e4a4994d15455781c712278e 100644 (file)
@@ -555,8 +555,9 @@ static int
 set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
             unsigned long size)
 {
-       int idx = 0;
+       __u32 idx = 0;
        __u32 startsect;
+       int ret;
 
        if (clustnum > 0)
                startsect = mydata->data_begin +
@@ -566,26 +567,45 @@ set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
 
        debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
 
-       if ((size / mydata->sect_size) > 0) {
-               if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) {
-                       debug("Error writing data\n");
+       if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
+               ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+               printf("FAT: Misaligned buffer address (%p)\n", buffer);
+
+               while (size >= mydata->sect_size) {
+                       memcpy(tmpbuf, buffer, mydata->sect_size);
+                       ret = disk_write(startsect++, 1, tmpbuf);
+                       if (ret != 1) {
+                               debug("Error writing data (got %d)\n", ret);
+                               return -1;
+                       }
+
+                       buffer += mydata->sect_size;
+                       size -= mydata->sect_size;
+               }
+       } else if (size >= mydata->sect_size) {
+               idx = size / mydata->sect_size;
+               ret = disk_write(startsect, idx, buffer);
+               if (ret != idx) {
+                       debug("Error writing data (got %d)\n", ret);
                        return -1;
                }
-       }
 
-       if (size % mydata->sect_size) {
-               __u8 tmpbuf[mydata->sect_size];
+               startsect += idx;
+               idx *= mydata->sect_size;
+               buffer += idx;
+               size -= idx;
+       }
 
-               idx = size / mydata->sect_size;
-               buffer += idx * mydata->sect_size;
-               memcpy(tmpbuf, buffer, size % mydata->sect_size);
+       if (size) {
+               ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 
-               if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
-                       debug("Error writing data\n");
+               memcpy(tmpbuf, buffer, size);
+               ret = disk_write(startsect, 1, tmpbuf);
+               if (ret != 1) {
+                       debug("Error writing data (got %d)\n", ret);
                        return -1;
                }
-
-               return 0;
        }
 
        return 0;