diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index c7f3e893..4bb88a90 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.h @@ -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); diff --git a/libntfs/bitmap.c b/libntfs/bitmap.c index 45e1c877..69d498fd 100644 --- a/libntfs/bitmap.c +++ b/libntfs/bitmap.c @@ -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); diff --git a/libntfs/lcnalloc.c b/libntfs/lcnalloc.c index 9fad9a80..46698642 100644 --- a/libntfs/lcnalloc.c +++ b/libntfs/lcnalloc.c @@ -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", diff --git a/libntfs/mft.c b/libntfs/mft.c index fc902219..c0a3cae4 100644 --- a/libntfs/mft.c +++ b/libntfs/mft.c @@ -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) diff --git a/libntfs/volume.c b/libntfs/volume.c index 0f7dd140..bfef1117 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -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. diff --git a/ntfsprogs/ntfsmount.c b/ntfsprogs/ntfsmount.c index 3d665095..f6603f05 100644 --- a/ntfsprogs/ntfsmount.c +++ b/ntfsprogs/ntfsmount.c @@ -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,