[Ocfs2-tools-devel] [PATCH 1/1] Add truncate for "clear sparse"
function in tunefs,
tao.ma
tao.ma at oracle.com
Tue Nov 13 17:53:40 PST 2007
Mark Fasheh wrote:
> This patch fails the following scenario:
>
> $ ./reserve_space foo resv 0 0 $[10*1024*1024]File:
> foo
> cmd = foo
> l_whence = 0
> l_start = 0
> l_len = 10485760
>
> $ ./truncate foo 65536
> Truncating foo to 65536
>
> [reserve_space and truncate are taken from ocfs2-test]
>
> $ debugfs.ocfs2 /dev/sde2
> debugfs: stat foo
> Inode: 72890 Mode: 0644 Generation: 4289956910 (0xffb38c2e)
> FS Generation: 2776784650 (0xa5825f0a)
> Type: Regular Attr: 0x0 Flags: Valid
> User: 0 (root) Group: 0 (root) Size: 65536
> Links: 1 Clusters: 2560
> ctime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
> atime: 0x4739f671 -- Wed Dec 31 16:00:00 1969
> mtime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
> dtime: 0x0 -- Wed Dec 31 16:00:00 1969
> ctime_nsec: 0x3897a5e6 -- 949462502
> atime_nsec: 0x010a910e -- 17469710
> mtime_nsec: 0x3897a5e6 -- 949462502
> Last Extblk: 0
> Sub Alloc Slot: 0 Sub Alloc Bit: 185
> Tree Depth: 0 Count: 243 Next Free Rec: 1
> ## Offset Clusters Block# Flags
> 0 0 2560 96769 0x0
>
> $ tunefs.ocfs2 --fs-features=nounwritten /dev/sde2
> tunefs.ocfs2 1.4.0
> We have 9688495 clusters free and need 0 clusters for sparse files, 0
> clusters for more extent blocks and will need to clear 0 clusters of
> unwritten extents.
> Modify feature "nounwritten" for the volume
> Proceed (y/N): y
> Wrote Superblock
>
> $ debugfs.ocfs2 /dev/sde2debugfs.ocfs2 1.4.0
> debugfs: stat foo
> Inode: 72890 Mode: 0644 Generation: 4289956910 (0xffb38c2e)
> FS Generation: 2776784650 (0xa5825f0a)
> Type: Regular Attr: 0x0 Flags: Valid
> User: 0 (root) Group: 0 (root) Size: 65536
> Links: 1 Clusters: 2560
> ctime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
> atime: 0x4739f671 -- Wed Dec 31 16:00:00 1969
> mtime: 0x4739f672 -- Wed Dec 31 16:00:00 1969
> dtime: 0x0 -- Wed Dec 31 16:00:00 1969
> ctime_nsec: 0x3897a5e6 -- 949462502
> atime_nsec: 0x010a910e -- 17469710
> mtime_nsec: 0x3897a5e6 -- 949462502
> Last Extblk: 0
> Sub Alloc Slot: 0 Sub Alloc Bit: 185
> Tree Depth: 0 Count: 243 Next Free Rec: 1
> ## Offset Clusters Block# Flags
> 0 0 2560 96769 0x0
>
> So, the existing unwritten extents code zero's the entire extent because
> it's straddling i_size. Otherwise, the file isn't getting truncate which is
> not what I think you expected to happen.
>
> I found the problem, it's noted below...
>
>
> On Tue, Nov 13, 2007 at 04:41:26PM +0800, tao.ma wrote:
>
>> take 2
>>
>> Modification from V1 to V2:
>> Move truncate process to the end of operation.
>> We may need to truncate an unwritten extent sometime. So if we truncate the file
>> first, the unwritten extent information stored in clear_ctxt may be outdated.
>> So move it to the end.
>>
>> Signed-off-by: Tao Ma <tao.ma at oracle.com>
>> ---
>> tunefs.ocfs2/sparse_file.c | 65 +++++++++++++++++++++++++++++++++++++++-----
>> 1 files changed, 58 insertions(+), 7 deletions(-)
>>
>> diff --git a/tunefs.ocfs2/sparse_file.c b/tunefs.ocfs2/sparse_file.c
>> index 8ef6710..06782f6 100644
>> --- a/tunefs.ocfs2/sparse_file.c
>> +++ b/tunefs.ocfs2/sparse_file.c
>> @@ -67,10 +67,14 @@ struct unwritten_list {
>> * unwritten_list. Since filling up a hole may need a new extent record and
>> * lead to some new extent block, the total hole number in the sparse file
>> * will also be recorded.
>> + *
>> + * Some sparse file may also have some clusters which exceed the limit of
>> + * i_size, and they should be truncated.
>> */
>> struct sparse_file {
>> uint64_t blkno;
>> uint32_t holes_num;
>> + int truncate;
>> struct hole_list *holes;
>> struct unwritten_list *unwritten;
>> struct sparse_file *next;
>> @@ -208,6 +212,7 @@ static void list_sparse_iterate(void *priv_data,
>> * Iterate a file.
>> * Call "func" when we meet with a hole.
>> * Call "unwritten_func" when we meet with unwritten clusters.
>> + * Call "seen_exceed" when we see some clusters exceed i_size.
>> */
>> static errcode_t iterate_file(ocfs2_filesys *fs,
>> struct ocfs2_dinode *di,
>> @@ -218,10 +223,12 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>> uint32_t start,
>> uint32_t len,
>> uint64_t p_start),
>> + void (*seen_exceed)(void *priv_data),
>> void *priv_data)
>> {
>> errcode_t ret;
>> uint32_t clusters, v_cluster = 0, p_cluster, num_clusters;
>> + uint32_t get_clusters = 0;
>> uint64_t p_blkno;
>> uint16_t extent_flags;
>> ocfs2_cached_inode *ci = NULL;
>> @@ -250,7 +257,8 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>>
>> if (func)
>> func(priv_data, v_cluster, num_clusters);
>> - }
>> + } else
>> + get_clusters += num_clusters;
>>
>> if ((extent_flags & OCFS2_EXT_UNWRITTEN) && unwritten_func) {
>> p_blkno = ocfs2_clusters_to_blocks(fs, p_cluster);
>> @@ -261,6 +269,17 @@ static errcode_t iterate_file(ocfs2_filesys *fs,
>> v_cluster += num_clusters;
>> }
>>
>> + /*
>> + * If there are some extra clusters we need to record them.
>> + * There are 2 scenarios:
>> + * 1. there are more cotinguous clusters allocated in which case
>> + * get_clusters > di->i_clusters
>> + * 2. there are more clusters allocated after i_size in which case
>> + * get_clusters < di->i_clusters
>> + */
>> + if (get_clusters != di->i_clusters && seen_exceed)
>> + seen_exceed(priv_data);
>>
>
> This needs to compare against 'clusters' (as computed above in that
> function), not 'i_clusters'. i_clusters will always equal the total number
> of clusters in the inode, irregardless of i_size, so you won't hit this in
> the case that the last extent includes i_size, but goes too far.
>
Yeah, this fails in some scenario, but "clusters" computed above also
doesn't work since for a file with holes, get_clusters(calculated from
the allocated clusters) may be equal to "clusters". e.g, there is a 1
cluster hole before i_size and 1 cluster allocated after i_size.
So let me generalize all the situation we may meet with for a truncate.
1. Non sparse file: Unwritten extent and written extent are calculated
the same process.
the check should be get_clusters > clusters or v_cluster > clusters
2. Sparse file:
1) We have iterate to the end of 'clusters' but there are still
allocated clusters after i_size, in which case
get_clusters < di->i_clusters.
2) the i_size resides in the last extent rec and it has been allocated
with more clusters: v_cluster > clusters
So the general check should be:
if ((get_clusters <di->i_clusters || v_cluster > clusters) && seen_exceed)
Agree?
More information about the Ocfs2-tools-devel
mailing list