[PATCH 3/3] Filesystem: PR1893: Fix write and truncate handler

sebastian.huber at embedded-brains.de sebastian.huber at embedded-brains.de
Mon May 7 15:09:06 UTC 2012


From: Sebastian Huber <sebastian.huber at embedded-brains.de>

Space that grows due to truncate or write offsets beyond the current
file size must be zero filled.
---
 cpukit/libfs/src/dosfs/fat.c                |   36 ++++++++++++++++++++++++++
 cpukit/libfs/src/dosfs/fat.h                |    6 ++++
 cpukit/libfs/src/dosfs/fat_fat_operations.c |   30 +++++++++++++++------
 cpukit/libfs/src/dosfs/fat_fat_operations.h |    3 +-
 cpukit/libfs/src/dosfs/fat_file.c           |   36 +++++++++++++++++++++-----
 cpukit/libfs/src/dosfs/fat_file.h           |    1 +
 cpukit/libfs/src/dosfs/msdos_file.c         |   37 ++++++++++++++++-----------
 cpukit/libfs/src/dosfs/msdos_misc.c         |    4 +-
 cpukit/libfs/src/imfs/memfile.c             |   21 +++++++++++++--
 9 files changed, 137 insertions(+), 37 deletions(-)

diff --git a/cpukit/libfs/src/dosfs/fat.c b/cpukit/libfs/src/dosfs/fat.c
index 39a7bac..a7030fc 100644
--- a/cpukit/libfs/src/dosfs/fat.c
+++ b/cpukit/libfs/src/dosfs/fat.c
@@ -263,6 +263,42 @@ _fat_block_write(
     return cmpltd;
 }
 
+int
+_fat_block_zero(
+    rtems_filesystem_mount_table_entry_t *mt_entry,
+    uint32_t                              start,
+    uint32_t                              offset,
+    uint32_t                              count)
+{
+    int                 rc = RC_OK;
+    fat_fs_info_t      *fs_info = mt_entry->fs_info;
+    uint32_t            blk  = start;
+    uint32_t            ofs = offset;
+    rtems_bdbuf_buffer *block = NULL;
+    uint32_t            c = 0;
+
+    while(count > 0)
+    {
+        c = MIN(count, (fs_info->vol.bps - ofs));
+
+        if (c == fs_info->vol.bps)
+            rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_GET, &block);
+        else
+            rc = fat_buf_access(fs_info, blk, FAT_OP_TYPE_READ, &block);
+        if (rc != RC_OK)
+            return -1;
+
+        memset((block->buffer + ofs), 0, c);
+
+        fat_buf_mark_modified(fs_info);
+
+        count -= c;
+        blk++;
+        ofs = 0;
+    }
+    return 0;
+}
+
 /* _fat_block_release --
  *     This function works around the hack that hold a bdbuf and does
  *     not release it.
diff --git a/cpukit/libfs/src/dosfs/fat.h b/cpukit/libfs/src/dosfs/fat.h
index 204095c..e55d23d 100644
--- a/cpukit/libfs/src/dosfs/fat.h
+++ b/cpukit/libfs/src/dosfs/fat.h
@@ -465,6 +465,12 @@ _fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry,
                  const void                           *buff);
 
 int
+_fat_block_zero(rtems_filesystem_mount_table_entry_t *mt_entry,
+                 uint32_t                              start,
+                 uint32_t                              offset,
+                 uint32_t                              count);
+
+int
 _fat_block_release(rtems_filesystem_mount_table_entry_t *mt_entry);
 
 ssize_t
diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.c b/cpukit/libfs/src/dosfs/fat_fat_operations.c
index c4f8280..756af99 100644
--- a/cpukit/libfs/src/dosfs/fat_fat_operations.c
+++ b/cpukit/libfs/src/dosfs/fat_fat_operations.c
@@ -46,7 +46,8 @@ fat_scan_fat_for_free_clusters(
     uint32_t                             *chain,
     uint32_t                              count,
     uint32_t                             *cls_added,
-    uint32_t                             *last_cl
+    uint32_t                             *last_cl,
+    bool                                  zero_fill
     )
 {
     int            rc = RC_OK;
@@ -113,14 +114,16 @@ fat_scan_fat_for_free_clusters(
 
                 rc = fat_set_fat_cluster(mt_entry, save_cln, cl4find);
                 if ( rc != RC_OK )
-                {
-                    /* cleanup activity */
-                    fat_free_fat_clusters_chain(mt_entry, (*chain));
-                    /* trying to save last allocated cluster for future use */
-                    fat_set_fat_cluster(mt_entry, cl4find, FAT_GENFAT_FREE);
-                    fat_buf_release(fs_info);
-                    return rc;
-                }
+                    goto cleanup;
+            }
+
+            if (zero_fill) {
+                uint32_t sec = fat_cluster_num_to_sector_num(mt_entry,
+                                                             cl4find);
+
+                rc = _fat_block_zero(mt_entry, sec, 0, fs_info->vol.bpc);
+                if ( rc != RC_OK )
+                    goto cleanup;
             }
 
             save_cln = cl4find;
@@ -150,6 +153,15 @@ fat_scan_fat_for_free_clusters(
     *last_cl = save_cln;
     fat_buf_release(fs_info);
     return RC_OK;
+
+cleanup:
+
+    /* cleanup activity */
+    fat_free_fat_clusters_chain(mt_entry, (*chain));
+    /* trying to save last allocated cluster for future use */
+    fat_set_fat_cluster(mt_entry, cl4find, FAT_GENFAT_FREE);
+    fat_buf_release(fs_info);
+    return rc;
 }
 
 /* fat_free_fat_clusters_chain --
diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.h b/cpukit/libfs/src/dosfs/fat_fat_operations.h
index d516057..be99798 100644
--- a/cpukit/libfs/src/dosfs/fat_fat_operations.h
+++ b/cpukit/libfs/src/dosfs/fat_fat_operations.h
@@ -43,7 +43,8 @@ fat_scan_fat_for_free_clusters(
     uint32_t                             *chain,
     uint32_t                              count,
     uint32_t                             *cls_added,
-    uint32_t                             *last_cl
+    uint32_t                             *last_cl,
+    bool                                  zero_fill
 );
 
 int
diff --git a/cpukit/libfs/src/dosfs/fat_file.c b/cpukit/libfs/src/dosfs/fat_file.c
index 01dabc8..a10347c 100644
--- a/cpukit/libfs/src/dosfs/fat_file.c
+++ b/cpukit/libfs/src/dosfs/fat_file.c
@@ -381,18 +381,18 @@ fat_file_write(
     uint32_t       sec = 0;
     uint32_t       byte = 0;
     uint32_t       c = 0;
+    bool           zero_fill = start > fat_fd->fat_file_size;
 
     if ( count == 0 )
         return cmpltd;
 
-    if ( start > fat_fd->fat_file_size )
-        rtems_set_errno_and_return_minus_one( EIO );
+    if (start >= fat_fd->size_limit)
+        rtems_set_errno_and_return_minus_one(EFBIG);
 
-    if ((count > fat_fd->size_limit) ||
-        (start > fat_fd->size_limit - count))
-        rtems_set_errno_and_return_minus_one( EIO );
+    if (count > fat_fd->size_limit - start)
+        count = fat_fd->size_limit - start;
 
-    rc = fat_file_extend(mt_entry, fat_fd, start + count, &c);
+    rc = fat_file_extend(mt_entry, fat_fd, zero_fill, start + count, &c);
     if (rc != RC_OK)
         return rc;
 
@@ -475,6 +475,7 @@ int
 fat_file_extend(
     rtems_filesystem_mount_table_entry_t *mt_entry,
     fat_file_fd_t                        *fat_fd,
+    bool                                  zero_fill,
     uint32_t                              new_length,
     uint32_t                             *a_length
     )
@@ -509,6 +510,27 @@ fat_file_extend(
     else
         bytes2add = 0;
 
+    if (zero_fill && bytes_remain > 0) {
+        uint32_t start = fat_fd->fat_file_size;
+        uint32_t cl_start = start >> fs_info->vol.bpc_log2;
+        uint32_t ofs = start & (fs_info->vol.bpc - 1);
+        uint32_t cur_cln;
+        uint32_t sec;
+        uint32_t byte;
+
+        rc = fat_file_lseek(mt_entry, fat_fd, cl_start, &cur_cln);
+        if (rc != RC_OK)
+            return rc;
+
+        sec = fat_cluster_num_to_sector_num(mt_entry, cur_cln);
+        sec += ofs >> fs_info->vol.sec_log2;
+        byte = ofs & (fs_info->vol.bps - 1);
+
+        rc = _fat_block_zero(mt_entry, sec, byte, bytes_remain);
+        if (rc != RC_OK)
+            return rc;
+    }
+
     /*
      * if in last cluster allocated for the file there is enough room to
      * handle extention (hence we don't need to add even one cluster to the
@@ -520,7 +542,7 @@ fat_file_extend(
     cls2add = ((bytes2add - 1) >> fs_info->vol.bpc_log2) + 1;
 
     rc = fat_scan_fat_for_free_clusters(mt_entry, &chain, cls2add,
-                                        &cls_added, &last_cl);
+                                        &cls_added, &last_cl, zero_fill);
 
     /* this means that low level I/O error occured */
     if (rc != RC_OK)
diff --git a/cpukit/libfs/src/dosfs/fat_file.h b/cpukit/libfs/src/dosfs/fat_file.h
index 01ab73e..30614c8 100644
--- a/cpukit/libfs/src/dosfs/fat_file.h
+++ b/cpukit/libfs/src/dosfs/fat_file.h
@@ -158,6 +158,7 @@ fat_file_write(rtems_filesystem_mount_table_entry_t *mt_entry,
 int
 fat_file_extend(rtems_filesystem_mount_table_entry_t *mt_entry,
                 fat_file_fd_t                        *fat_fd,
+                bool                                  zero_fill,
                 uint32_t                              new_length,
                 uint32_t                             *a_length);
 
diff --git a/cpukit/libfs/src/dosfs/msdos_file.c b/cpukit/libfs/src/dosfs/msdos_file.c
index 6eb9b79..f68e44f 100644
--- a/cpukit/libfs/src/dosfs/msdos_file.c
+++ b/cpukit/libfs/src/dosfs/msdos_file.c
@@ -203,7 +203,7 @@ msdos_file_stat(
 }
 
 /* msdos_file_ftruncate --
- *     Truncate the file (if new length is greater then current do nothing).
+ *     Truncate the file.
  *
  * PARAMETERS:
  *     iop    - file control block
@@ -219,31 +219,38 @@ msdos_file_ftruncate(rtems_libio_t *iop, off_t length)
     rtems_status_code  sc = RTEMS_SUCCESSFUL;
     msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
     fat_file_fd_t     *fat_fd = iop->pathinfo.node_access;
-
-    if (length >= fat_fd->fat_file_size)
-        return RC_OK;
+    uint32_t old_length;
 
     sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
                                 MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
     if (sc != RTEMS_SUCCESSFUL)
         rtems_set_errno_and_return_minus_one(EIO);
 
-    rc = fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, length);
-    if (rc != RC_OK)
-    {
-        rtems_semaphore_release(fs_info->vol_sema);
-        return rc;
+    old_length = fat_fd->fat_file_size;
+    if (length < old_length) {
+        rc = fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, length);
+    } else {
+        uint32_t new_length;
+
+        rc = fat_file_extend(iop->pathinfo.mt_entry,
+                             fat_fd,
+                             true,
+                             length,
+                             &new_length);
+        if (rc == RC_OK && length != new_length) {
+            fat_file_truncate(iop->pathinfo.mt_entry, fat_fd, old_length);
+            errno = ENOSPC;
+            rc = -1;
+        }
     }
 
-    /*
-     * fat_file_truncate do nothing if new length >= fat-file size, so update
-     * file size only if length < fat-file size
-     */
-    if (length < fat_fd->fat_file_size)
+    if (rc == RC_OK) {
         fat_fd->fat_file_size = length;
+    }
 
     rtems_semaphore_release(fs_info->vol_sema);
-    return RC_OK;
+
+    return rc;
 }
 
 /* msdos_file_sync --
diff --git a/cpukit/libfs/src/dosfs/msdos_misc.c b/cpukit/libfs/src/dosfs/msdos_misc.c
index f27b4ae..d11bd83 100644
--- a/cpukit/libfs/src/dosfs/msdos_misc.c
+++ b/cpukit/libfs/src/dosfs/msdos_misc.c
@@ -1411,8 +1411,8 @@ int msdos_find_name_in_fat_file(
 #if MSDOS_FIND_PRINT
             printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset);
 #endif
-            ret = fat_file_extend (mt_entry, fat_fd, empty_space_offset * bts2rd,
-                                   &new_length);
+            ret = fat_file_extend (mt_entry, fat_fd, false,
+                                   empty_space_offset * bts2rd, &new_length);
 
             if (ret != RC_OK)
               return ret;
diff --git a/cpukit/libfs/src/imfs/memfile.c b/cpukit/libfs/src/imfs/memfile.c
index d836172..b279787 100644
--- a/cpukit/libfs/src/imfs/memfile.c
+++ b/cpukit/libfs/src/imfs/memfile.c
@@ -33,6 +33,7 @@
  */
 MEMFILE_STATIC int IMFS_memfile_extend(
    IMFS_jnode_t  *the_jnode,
+   bool           zero_fill,
    off_t          new_length
 );
 
@@ -195,7 +196,7 @@ int memfile_ftruncate(
    */
 
   if ( length > the_jnode->info.file.size )
-    return IMFS_memfile_extend( the_jnode, length );
+    return IMFS_memfile_extend( the_jnode, true, length );
 
   /*
    *  The in-memory files do not currently reclaim memory until the file is
@@ -218,12 +219,14 @@ int memfile_ftruncate(
  */
 MEMFILE_STATIC int IMFS_memfile_extend(
    IMFS_jnode_t  *the_jnode,
+   bool           zero_fill,
    off_t          new_length
 )
 {
   unsigned int   block;
   unsigned int   new_blocks;
   unsigned int   old_blocks;
+  unsigned int   offset;
 
   /*
    *  Perform internal consistency checks
@@ -248,12 +251,22 @@ MEMFILE_STATIC int IMFS_memfile_extend(
    */
   new_blocks = new_length / IMFS_MEMFILE_BYTES_PER_BLOCK;
   old_blocks = the_jnode->info.file.size / IMFS_MEMFILE_BYTES_PER_BLOCK;
+  offset = the_jnode->info.file.size - old_blocks * IMFS_MEMFILE_BYTES_PER_BLOCK;
 
   /*
    *  Now allocate each of those blocks.
    */
   for ( block=old_blocks ; block<=new_blocks ; block++ ) {
-    if ( IMFS_memfile_addblock( the_jnode, block ) ) {
+    if ( !IMFS_memfile_addblock( the_jnode, block ) ) {
+       if ( zero_fill ) {
+          size_t count = IMFS_MEMFILE_BYTES_PER_BLOCK - offset;
+          block_p *block_ptr =
+            IMFS_memfile_get_block_pointer( the_jnode, block, 0 );
+
+          memset( &(*block_ptr) [offset], 0, count);
+          offset = 0;
+       }
+    } else {
        for ( ; block>=old_blocks ; block-- ) {
          IMFS_memfile_remove_block( the_jnode, block );
        }
@@ -622,7 +635,9 @@ MEMFILE_STATIC ssize_t IMFS_memfile_write(
 
   last_byte = start + my_length;
   if ( last_byte > the_jnode->info.file.size ) {
-    status = IMFS_memfile_extend( the_jnode, last_byte );
+    bool zero_fill = start > the_jnode->info.file.size;
+
+    status = IMFS_memfile_extend( the_jnode, zero_fill, last_byte );
     if ( status )
       return status;
   }
-- 
1.6.4.2




More information about the devel mailing list