From a97c661efeeea258a83889addeb78f0054d0c00b Mon Sep 17 00:00:00 2001 From: szaka Date: Wed, 12 Sep 2007 20:51:06 +0000 Subject: [PATCH] statfs optimization: O(1) free cluster calculation (David Fox, Szabolcs Szakacsits) --- include/ntfs-3g/volume.h | 3 ++ libntfs-3g/lcnalloc.c | 83 ++++++++++++++++++++++++---------------- libntfs-3g/mft.c | 9 +++-- src/ntfs-3g.c | 23 ++++++----- 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index e0d6aa06..3020665f 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -212,6 +212,9 @@ struct _ntfs_volume { s32 attrdef_len; /* Size of the attribute definition table in bytes. */ + s64 free_clusters; /* Track the number of free clusters which + greatly improves statfs() performance */ + /* Temp: for directory handling */ void *private_data; /* ntfs_dir for . */ void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */ diff --git a/libntfs-3g/lcnalloc.c b/libntfs-3g/lcnalloc.c index df834750..073141d0 100644 --- a/libntfs-3g/lcnalloc.c +++ b/libntfs-3g/lcnalloc.c @@ -305,6 +305,11 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, /* Allocate the bitmap bit. */ *byte |= bit; writeback = 1; + if (vol->free_clusters <= 0) + ntfs_log_error("Non-positive free clusters " + "(%lld)!\n", vol->free_clusters); + else + vol->free_clusters--; /* * Coalesce with previous run if adjacent LCNs. @@ -490,6 +495,9 @@ err_ret: */ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) { + s64 nr_freed = 0; + int ret = -1; + ntfs_log_trace("Entering.\n"); for (; rl->length; rl++) { @@ -497,15 +505,26 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", (long long)rl->lcn, (long long)rl->length); - if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na, - rl->lcn, rl->length)) { - int eo = errno; - ntfs_log_trace("Eeek! Deallocation of clusters failed.\n"); - errno = eo; - return -1; + if (rl->lcn >= 0) { + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, + rl->length)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", rl->lcn, + rl->length); + goto out; + } + nr_freed += rl->length ; } } - return 0; + + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; } /** @@ -527,7 +546,8 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) { runlist *rl; - s64 nr_freed, delta, to_free; + s64 delta, to_free, nr_freed = 0; + int ret = -1; if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || (count < 0 && count != -1)) { @@ -543,12 +563,13 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) if (!rl) { if (errno == ENOENT) return 0; - else - return -1; + return -1; } if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { errno = EIO; + ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, + (long long)rl->lcn); return -1; } @@ -563,14 +584,10 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) if (rl->lcn != LCN_HOLE) { /* Do the actual freeing of the clusters in this run. */ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, - to_free)) + to_free)) return -1; - /* We have freed @to_free real clusters. */ nr_freed = to_free; - } else { - /* No real clusters were freed. */ - nr_freed = 0; - } + } /* Go to the next run and adjust the number of clusters left to free. */ ++rl; @@ -586,11 +603,10 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) // list support! (AIA) if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! invalid lcn (= %lli). Should attempt " - "to map runlist! Leaving inconsistent " - "metadata!\n", (long long)rl->lcn); errno = EIO; - return -1; + ntfs_log_perror("%s: Invalid lcn (%lli)", + __FUNCTION__, (long long)rl->lcn); + goto out; } /* The number of clusters in this run that need freeing. */ @@ -599,18 +615,13 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) to_free = count; if (rl->lcn != LCN_HOLE) { - /* Do the actual freeing of the clusters in the run. */ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, to_free)) { - int eo = errno; - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! bitmap clear run failed. " - "Leaving inconsistent metadata!\n"); - errno = eo; - return -1; + ntfs_log_perror("%s: Clearing bitmap run failed", + __FUNCTION__); + goto out; } - /* We have freed @to_free real clusters. */ nr_freed += to_free; } @@ -620,12 +631,18 @@ int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) if (count != -1 && count != 0) { // FIXME: Eeek! BUG() - ntfs_log_trace("Eeek! count still not zero (= %lli). Leaving " - "inconsistent metadata!\n", (long long)count); errno = EIO; - return -1; + ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, + (long long)count); + goto out; } - /* Done. Return the number of actual clusters that were freed. */ - return nr_freed; + ret = nr_freed; +out: + vol->free_clusters += nr_freed ; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; } diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 98c0c686..7171cf6f 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -568,7 +568,7 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) { LCN lcn; s64 ll = 0; /* silence compiler warning */ - ntfs_attr *mftbmp_na, *lcnbmp_na; + ntfs_attr *mftbmp_na; runlist_element *rl, *rl2 = NULL; /* silence compiler warning */ ntfs_attr_search_ctx *ctx; MFT_RECORD *m = NULL; /* silence compiler warning */ @@ -578,7 +578,6 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol) BOOL mp_rebuilt = FALSE; mftbmp_na = vol->mftbmp_na; - lcnbmp_na = vol->lcnbmp_na; /* * Determine the last lcn of the mft bitmap. The allocated size of the * mft bitmap cannot be zero so we are ok to do this. @@ -726,9 +725,11 @@ undo_alloc: rl->lcn = rl[1].lcn; rl->length = 0; - /* Deallocate the cluster. */ - if (ntfs_bitmap_clear_bit(lcnbmp_na, lcn)) + /* FIXME: use an ntfs_cluster_free_* function */ + if (ntfs_bitmap_clear_bit(vol->lcnbmp_na, lcn)) ntfs_log_error("Failed to free cluster.%s\n", es); + else + vol->free_clusters++; if (mp_rebuilt) { if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(a->mapping_pairs_offset), diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 3cafffd2..12c7d590 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -106,7 +106,6 @@ typedef enum { typedef struct { ntfs_volume *vol; int state; - long free_clusters; long free_mft; unsigned int uid; unsigned int gid; @@ -123,8 +122,6 @@ typedef struct { } ntfs_fuse_context_t; typedef enum { - NF_FreeClustersOutdate = (1 << 0), /* Information about amount of - free clusters is outdated. */ NF_FreeMFTOutdate = (1 << 1), /* Information about amount of free MFT records is outdated. */ } ntfs_fuse_state_bits; @@ -167,8 +164,8 @@ static const char *usage_msg = static __inline__ void ntfs_fuse_mark_free_space_outdated(void) { - /* Mark information about free MFT record and clusters outdated. */ - ctx->state |= (NF_FreeClustersOutdate | NF_FreeMFTOutdate); + /* Mark information about free MFT records outdated. */ + ctx->state |= NF_FreeMFTOutdate; } /** @@ -221,14 +218,12 @@ static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, s64 numof_inode) return nr_free; } -static long ntfs_fuse_get_nr_free_clusters(ntfs_volume *vol) +static long ntfs_get_nr_free_clusters(ntfs_volume *vol) { u8 *buf; long nr_free = 0; s64 br, total = 0; - if (!(ctx->state & NF_FreeClustersOutdate)) - return ctx->free_clusters; buf = ntfs_malloc(vol->cluster_size); if (!buf) return -errno; @@ -248,8 +243,6 @@ static long ntfs_fuse_get_nr_free_clusters(ntfs_volume *vol) free(buf); if (!total || br < 0) return -errno; - ctx->free_clusters = nr_free; - ctx->state &= ~(NF_FreeClustersOutdate); return nr_free; } @@ -292,7 +285,7 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)), sfs->f_blocks = vol->nr_clusters; /* Free data blocks in file system in units of f_bsize. */ - size = ntfs_fuse_get_nr_free_clusters(vol); + size = vol->free_clusters; if (size < 0) size = 0; @@ -1655,7 +1648,7 @@ static int ntfs_fuse_init(void) return -1; *ctx = (ntfs_fuse_context_t) { - .state = NF_FreeClustersOutdate | NF_FreeMFTOutdate, + .state = NF_FreeMFTOutdate, .uid = getuid(), .gid = getgid(), .fmask = 0, @@ -2233,6 +2226,12 @@ int main(int argc, char *argv[]) if (!ntfs_open(opts.device, opts.mnt_point, use_blkdev)) goto err_out; + ctx->vol->free_clusters = ntfs_get_nr_free_clusters(ctx->vol); + if (ctx->vol->free_clusters < 0) { + ntfs_log_perror("Failed to read NTFS $Bitmap"); + goto err_out; + } + if (use_blkdev) { set_fuseblk_options(parsed_options); set_user_mount_option(parsed_options, uid);