From 3d0c96c7f34204aa4386cd9c1cd4a3e7977d52a0 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 7 May 2012 16:31:15 +0200 Subject: Filesystem: PR1893: Fix write and truncate handler 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 39a7bacb24..a7030fc78c 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 204095cae2..e55d23d845 100644 --- a/cpukit/libfs/src/dosfs/fat.h +++ b/cpukit/libfs/src/dosfs/fat.h @@ -464,6 +464,12 @@ _fat_block_write(rtems_filesystem_mount_table_entry_t *mt_entry, uint32_t count, 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); diff --git a/cpukit/libfs/src/dosfs/fat_fat_operations.c b/cpukit/libfs/src/dosfs/fat_fat_operations.c index c4f82805e8..756af99c90 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 d516057ad0..be997980d7 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 01dabc8f80..a10347ccc4 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 01ab73ef98..30614c8f46 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 6eb9b7953a..f68e44f948 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 f27b4aea9c..d11bd83f52 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 d836172884..b2797875fc 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; } -- cgit v1.2.3