Make libntfs keep track number of free clusters and MFT records.

Thanks for idea to David Fox and Szabolcs Szakacsits.
This commit is contained in:
Yura Pakhuchiy 2007-09-14 12:59:55 +03:00
parent f5b9888eb6
commit e248e6b986
6 changed files with 132 additions and 128 deletions

View File

@ -208,10 +208,8 @@ struct _ntfs_volume {
s32 attrdef_len; /* Size of the attribute definition table in
bytes. */
/* Temp: for directory handling */
void *private_data; /* ntfs_dir for . */
void *private_bmp1; /* ntfs_bmp for $MFT/$BITMAP */
void *private_bmp2; /* ntfs_bmp for $Bitmap */
long nr_free_clusters; /* This two are self explaining. */
long nr_free_mft_records;
};
extern ntfs_volume *ntfs_volume_alloc(void);

View File

@ -58,7 +58,8 @@
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
s64 count, int value)
{
s64 bufsize, br;
ntfs_volume *vol = na->ni->vol;
s64 bufsize, br, left = count;
u8 *buf, *lastbyte_buf;
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err;
@ -95,7 +96,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
return -1;
}
/* and set or clear the appropriate bits in it. */
while ((bit & 7) && count--) {
while ((bit & 7) && left--) {
if (value)
*buf |= 1 << bit++;
else
@ -105,14 +106,14 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
start_bit = (start_bit + 7) & ~7;
}
/* Loop until @count reaches zero. */
/* Loop until @left reaches zero. */
lastbyte = 0;
lastbyte_buf = NULL;
bit = count & 7;
bit = left & 7;
do {
/* If there is a last partial byte... */
if (count > 0 && bit) {
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
if (left > 0 && bit) {
lastbyte_pos = ((left + 7) >> 3) + firstbyte;
if (!lastbyte_pos) {
// FIXME: Eeek! BUG!
ntfs_log_trace("lastbyte is zero. Leaving "
@ -125,7 +126,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
lastbyte_buf = buf + lastbyte_pos - 1;
/* read the byte in... */
br = ntfs_attr_pread(na, (start_bit + count) >>
br = ntfs_attr_pread(na, (start_bit + left) >>
3, 1, lastbyte_buf);
if (br != 1) {
// FIXME: Eeek! We need rollback! (AIA)
@ -137,7 +138,7 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
goto free_err_out;
}
/* and set/clear the appropriate bits in it. */
while (bit && count--) {
while (bit && left--) {
if (value)
*lastbyte_buf |= 1 << --bit;
else
@ -172,19 +173,33 @@ static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
*buf = value ? 0xff : 0;
}
start_bit += tmp;
count -= tmp;
if (bufsize > (tmp = (count + 7) >> 3))
left -= tmp;
if (bufsize > (tmp = (left + 7) >> 3))
bufsize = tmp;
if (lastbyte && count != 0) {
if (lastbyte && left != 0) {
// FIXME: Eeek! BUG!
ntfs_log_trace("Last buffer but count is not zero (= "
"%lli). Leaving inconsistent metadata."
"\n", (long long)count);
"\n", (long long)left);
err = EIO;
goto free_err_out;
}
} while (count > 0);
} while (left > 0);
/* Update free clusters and MFT records. */
if (na == vol->mftbmp_na) {
if (value)
vol->nr_free_mft_records -= count;
else
vol->nr_free_mft_records += count;
}
if (na == vol->lcnbmp_na) {
if (value)
vol->nr_free_clusters -= count;
else
vol->nr_free_clusters += count;
}
/* Done! */
free(buf);

View File

@ -287,6 +287,7 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
}
/* Allocate the bitmap bit. */
*byte |= bit;
vol->nr_free_clusters--;
/* We need to write this bitmap buffer back to disk! */
need_writeback = 1;
ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n",

View File

@ -609,6 +609,7 @@ static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol)
errno = EIO;
return -1;
}
vol->nr_free_clusters--;
/* Update the mft bitmap runlist. */
rl->length++;
rl[1].vcn++;
@ -833,6 +834,7 @@ static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol)
ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n");
return 0;
}
vol->nr_free_mft_records += 64; /* 8 bytes x 8 bits each. */
ntfs_log_error("Failed to write to mft bitmap.\n");
err = errno;
if (ll >= 0)

View File

@ -744,6 +744,88 @@ out:
return ret;
}
/**
* ntfs_volume_get_nr_free_mft_records - calculate number of free MFT records
* vol: ntfs volume for which perform calculations.
*
* This function initializes @vol->nr_free_mft_records. @vol->mftbmp_na should
* be already opened upon call to this function.
*
* Return 0 on success. On error return -1 with errno set appropriately and
* @vol->nr_free_mft_records is not touched in this case.
*/
static int ntfs_volume_get_nr_free_mft_records(ntfs_volume *vol)
{
long nr_free = vol->mft_na->data_size >> vol->mft_record_size_bits;
s64 br, total = 0;
u8 *buf;
buf = ntfs_malloc(vol->cluster_size);
if (!buf)
return -1;
while (1) {
int i, j;
br = ntfs_attr_pread(vol->mftbmp_na, total,
vol->cluster_size, buf);
if (br <= 0)
break;
total += br;
for (i = 0; i < br; i++)
for (j = 0; j < 8; j++)
if ((buf[i] >> j) & 1)
nr_free--;
}
free(buf);
if (!total || br < 0) {
ntfs_log_error("pread: %s\n", strerror(errno));
return -1;
}
vol->nr_free_mft_records = nr_free;
return 0;
}
/**
* ntfs_volume_get_nr_free_clusters - calculate number of free clusters
* vol: ntfs volume for which perform calculations.
*
* This function initializes @vol->nr_free_clusters. @vol->lcnbmp_na should be
* already opened upon call to this function.
*
* Return 0 on success. On error return -1 with errno set appropriately and
* @vol->nr_free_clusters is not touched in this case.
*/
static long ntfs_volume_get_nr_free_clusters(ntfs_volume *vol)
{
long nr_free = vol->nr_clusters;
s64 br, total = 0;
u8 *buf;
buf = ntfs_malloc(vol->cluster_size);
if (!buf)
return -1;
while (1) {
int i, j;
br = ntfs_attr_pread(vol->lcnbmp_na, total,
vol->cluster_size, buf);
if (br <= 0)
break;
total += br;
for (i = 0; i < br; i++)
for (j = 0; j < 8; j++)
if ((buf[i] >> j) & 1)
nr_free--;
}
free(buf);
if (!total || br < 0) {
ntfs_log_error("pread: %s\n", strerror(errno));
return -1;
}
vol->nr_free_clusters = nr_free;
return 0;
}
/**
* ntfs_device_mount - open ntfs volume
* @dev: device to open
@ -1127,6 +1209,15 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
ntfs_log_perror("Failed to close inode, leaking memory");
/* Initialize number of free clusters and MFT records. */
if (ntfs_volume_get_nr_free_mft_records(vol)) {
ntfs_log_perror("Failed to calculate number of free MFTs");
goto error_exit;
}
if (ntfs_volume_get_nr_free_clusters(vol)) {
ntfs_log_perror("Failed to calculate number of free clusters");
goto error_exit;
}
/*
* Check for dirty logfile and hibernated Windows.
* We care only about read-write mounts.

View File

@ -97,9 +97,6 @@ typedef struct {
char *mnt_point;
char *device;
char *locale;
int state;
long free_clusters;
long free_mft;
unsigned int uid;
unsigned int gid;
unsigned int fmask;
@ -119,13 +116,6 @@ typedef struct {
BOOL blkdev;
} 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;
#define NTFS_FUSE_OPT(t, p) { t, offsetof(ntfs_fuse_context_t, p), TRUE }
#define NTFS_FUSE_OPT_NEG(t, p) { t, offsetof(ntfs_fuse_context_t, p), FALSE }
#define NTFS_FUSE_OPT_VAL(t, p, v) { t, offsetof(ntfs_fuse_context_t, p), v }
@ -187,15 +177,6 @@ static char ntfs_fuse_default_options[] =
"default_permissions,allow_other,use_ino,kernel_cache,nonempty";
static ntfs_fuse_context_t *ctx;
/**
* ntfs_fuse_mark_free_space_outdated - forces free space recalculation
*/
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);
}
/**
* ntfs_fuse_is_named_data_stream - check path to be to named data stream
* @path: path to check
@ -221,73 +202,6 @@ static __inline__ void ntfs_fuse_update_times(ntfs_inode *ni,
ntfs_inode_update_times(ni, mask);
}
static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, long nr_free)
{
u8 *buf;
s64 br, total = 0;
if (!(ctx->state & NF_FreeMFTOutdate))
return ctx->free_mft;
buf = ntfs_malloc(vol->cluster_size);
if (!buf)
return -errno;
while (1) {
int i, j;
br = ntfs_attr_pread(vol->mftbmp_na, total,
vol->cluster_size, buf);
if (br <= 0)
break;
total += br;
for (i = 0; i < br; i++)
for (j = 0; j < 8; j++)
if ((buf[i] >> j) & 1)
nr_free--;
}
free(buf);
if (!total || br < 0) {
ntfs_log_error("pread: %s\n", strerror(errno));
return -errno;
}
ctx->free_mft = nr_free;
ctx->state &= ~(NF_FreeMFTOutdate);
return nr_free;
}
static long ntfs_fuse_get_nr_free_clusters(ntfs_volume *vol)
{
u8 *buf;
long nr_free = vol->nr_clusters;
s64 br, total = 0;
if (!(ctx->state & NF_FreeClustersOutdate))
return ctx->free_clusters;
buf = ntfs_malloc(vol->cluster_size);
if (!buf)
return -errno;
while (1) {
int i, j;
br = ntfs_attr_pread(vol->lcnbmp_na, total,
vol->cluster_size, buf);
if (br <= 0)
break;
total += br;
for (i = 0; i < br; i++)
for (j = 0; j < 8; j++)
if ((buf[i] >> j) & 1)
nr_free--;
}
free(buf);
if (!total || br < 0) {
ntfs_log_error("pread: %s\n", strerror(errno));
return -errno;
}
ctx->free_clusters = nr_free;
ctx->state &= ~(NF_FreeClustersOutdate);
return nr_free;
}
/**
* ntfs_fuse_statfs - return information about mounted NTFS volume
* @path: ignored (but fuse requires it)
@ -308,8 +222,6 @@ static long ntfs_fuse_get_nr_free_clusters(ntfs_volume *vol)
static int ntfs_fuse_statfs(const char *path __attribute__((unused)),
struct statvfs *sfs)
{
long size;
/* Optimal transfer block size. */
sfs->f_bsize = ctx->vol->cluster_size;
sfs->f_frsize = ctx->vol->cluster_size;
@ -319,20 +231,16 @@ static int ntfs_fuse_statfs(const char *path __attribute__((unused)),
* the total clusters.
*/
sfs->f_blocks = ctx->vol->nr_clusters;
/* Free data blocks in file system in units of f_bsize. */
size = ntfs_fuse_get_nr_free_clusters(ctx->vol);
if (size < 0)
size = 0;
/* Free blocks avail to non-superuser, same as above on NTFS. */
sfs->f_bavail = sfs->f_bfree = size;
/*
* Free data blocks and free data block available to non-superuser in
* file system in units of f_bsize.
*/
sfs->f_bavail = sfs->f_bfree = ctx->vol->nr_free_clusters;
/* Number of inodes in file system (at this point in time). */
size = ctx->vol->mft_na->data_size >> ctx->vol->mft_record_size_bits;
sfs->f_files = size;
sfs->f_files = ctx->vol->mft_na->data_size >>
ctx->vol->mft_record_size_bits;
/* Free inodes in fs (based on current total count). */
size = ntfs_fuse_get_nr_free_mft_records(ctx->vol, size);
if (size < 0)
size = 0;
sfs->f_ffree = size;
sfs->f_ffree = ctx->vol->nr_free_mft_records;
/* Maximum length of filenames. */
sfs->f_namemax = NTFS_MAX_NAME_LEN;
return 0;
@ -746,7 +654,6 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
}
res = total;
exit:
ntfs_fuse_mark_free_space_outdated();
if (res > 0)
ntfs_fuse_update_times(ni, NTFS_UPDATE_MTIME |
NTFS_UPDATE_CTIME);
@ -793,7 +700,6 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size)
NTFS_UPDATE_CTIME);
res = 0;
}
ntfs_fuse_mark_free_space_outdated();
ntfs_attr_close(na);
exit:
if (ni && ntfs_inode_close(ni))
@ -938,7 +844,6 @@ static int ntfs_fuse_mknod(const char *org_path, mode_t mode, dev_t dev)
else
res = ntfs_fuse_create_stream(path, stream_name,
stream_name_len);
ntfs_fuse_mark_free_space_outdated();
exit:
free(path);
if (stream_name_len)
@ -950,7 +855,6 @@ static int ntfs_fuse_symlink(const char *to, const char *from)
{
if (ntfs_fuse_is_named_data_stream(from))
return -EINVAL; /* n/a for named data streams. */
ntfs_fuse_mark_free_space_outdated();
return ntfs_fuse_create(from, S_IFLNK, 0, to);
}
@ -992,7 +896,6 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path)
res = -EIO;
goto exit;
}
ntfs_fuse_mark_free_space_outdated();
/* Create hard link. */
if (ntfs_link(ni, dir_ni, uname, uname_len))
res = -errno;
@ -1101,7 +1004,6 @@ static int ntfs_fuse_unlink(const char *org_path)
res = ntfs_fuse_rm(path);
else
res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len);
ntfs_fuse_mark_free_space_outdated();
free(path);
if (stream_name_len)
free(stream_name);
@ -1153,7 +1055,6 @@ static int ntfs_fuse_mkdir(const char *path,
{
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
ntfs_fuse_mark_free_space_outdated();
return ntfs_fuse_create(path, S_IFDIR, 0, NULL);
}
@ -1161,7 +1062,6 @@ static int ntfs_fuse_rmdir(const char *path)
{
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
ntfs_fuse_mark_free_space_outdated();
return ntfs_fuse_rm(path);
}
@ -1389,7 +1289,6 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
res = -EEXIST;
goto exit;
}
ntfs_fuse_mark_free_space_outdated();
if (!na) {
if (flags == XATTR_REPLACE) {
res = -ENODATA;
@ -1444,7 +1343,6 @@ static int ntfs_fuse_removexattr(const char *path, const char *name)
res = -ENODATA;
goto exit;
}
ntfs_fuse_mark_free_space_outdated();
if (ntfs_attr_rm(na))
res = -errno;
na = NULL;
@ -1543,7 +1441,6 @@ static int ntfs_fuse_init(void)
return -1;
*ctx = (ntfs_fuse_context_t) {
.state = NF_FreeClustersOutdate | NF_FreeMFTOutdate,
.uid = getuid(),
.gid = getgid(),
.fmask = 0111,