|
@@ -1537,6 +1537,129 @@ err_out:
|
|
|
|
|
|
#endif /* NTFS_RW */
|
|
|
|
|
|
+/**
|
|
|
+ * ntfs_bmap - map logical file block to physical device block
|
|
|
+ * @mapping: address space mapping to which the block to be mapped belongs
|
|
|
+ * @block: logical block to map to its physical device block
|
|
|
+ *
|
|
|
+ * For regular, non-resident files (i.e. not compressed and not encrypted), map
|
|
|
+ * the logical @block belonging to the file described by the address space
|
|
|
+ * mapping @mapping to its physical device block.
|
|
|
+ *
|
|
|
+ * The size of the block is equal to the @s_blocksize field of the super block
|
|
|
+ * of the mounted file system which is guaranteed to be smaller than or equal
|
|
|
+ * to the cluster size thus the block is guaranteed to fit entirely inside the
|
|
|
+ * cluster which means we do not need to care how many contiguous bytes are
|
|
|
+ * available after the beginning of the block.
|
|
|
+ *
|
|
|
+ * Return the physical device block if the mapping succeeded or 0 if the block
|
|
|
+ * is sparse or there was an error.
|
|
|
+ *
|
|
|
+ * Note: This is a problem if someone tries to run bmap() on $Boot system file
|
|
|
+ * as that really is in block zero but there is nothing we can do. bmap() is
|
|
|
+ * just broken in that respect (just like it cannot distinguish sparse from
|
|
|
+ * not available or error).
|
|
|
+ */
|
|
|
+static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)
|
|
|
+{
|
|
|
+ s64 ofs, size;
|
|
|
+ loff_t i_size;
|
|
|
+ LCN lcn;
|
|
|
+ unsigned long blocksize, flags;
|
|
|
+ ntfs_inode *ni = NTFS_I(mapping->host);
|
|
|
+ ntfs_volume *vol = ni->vol;
|
|
|
+ unsigned delta;
|
|
|
+ unsigned char blocksize_bits, cluster_size_shift;
|
|
|
+
|
|
|
+ ntfs_debug("Entering for mft_no 0x%lx, logical block 0x%llx.",
|
|
|
+ ni->mft_no, (unsigned long long)block);
|
|
|
+ if (ni->type != AT_DATA || !NInoNonResident(ni) || NInoEncrypted(ni)) {
|
|
|
+ ntfs_error(vol->sb, "BMAP does not make sense for %s "
|
|
|
+ "attributes, returning 0.",
|
|
|
+ (ni->type != AT_DATA) ? "non-data" :
|
|
|
+ (!NInoNonResident(ni) ? "resident" :
|
|
|
+ "encrypted"));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /* None of these can happen. */
|
|
|
+ BUG_ON(NInoCompressed(ni));
|
|
|
+ BUG_ON(NInoMstProtected(ni));
|
|
|
+ blocksize = vol->sb->s_blocksize;
|
|
|
+ blocksize_bits = vol->sb->s_blocksize_bits;
|
|
|
+ ofs = (s64)block << blocksize_bits;
|
|
|
+ read_lock_irqsave(&ni->size_lock, flags);
|
|
|
+ size = ni->initialized_size;
|
|
|
+ i_size = i_size_read(VFS_I(ni));
|
|
|
+ read_unlock_irqrestore(&ni->size_lock, flags);
|
|
|
+ /*
|
|
|
+ * If the offset is outside the initialized size or the block straddles
|
|
|
+ * the initialized size then pretend it is a hole unless the
|
|
|
+ * initialized size equals the file size.
|
|
|
+ */
|
|
|
+ if (unlikely(ofs >= size || (ofs + blocksize > size && size < i_size)))
|
|
|
+ goto hole;
|
|
|
+ cluster_size_shift = vol->cluster_size_bits;
|
|
|
+ down_read(&ni->runlist.lock);
|
|
|
+ lcn = ntfs_attr_vcn_to_lcn_nolock(ni, ofs >> cluster_size_shift, false);
|
|
|
+ up_read(&ni->runlist.lock);
|
|
|
+ if (unlikely(lcn < LCN_HOLE)) {
|
|
|
+ /*
|
|
|
+ * Step down to an integer to avoid gcc doing a long long
|
|
|
+ * comparision in the switch when we know @lcn is between
|
|
|
+ * LCN_HOLE and LCN_EIO (i.e. -1 to -5).
|
|
|
+ *
|
|
|
+ * Otherwise older gcc (at least on some architectures) will
|
|
|
+ * try to use __cmpdi2() which is of course not available in
|
|
|
+ * the kernel.
|
|
|
+ */
|
|
|
+ switch ((int)lcn) {
|
|
|
+ case LCN_ENOENT:
|
|
|
+ /*
|
|
|
+ * If the offset is out of bounds then pretend it is a
|
|
|
+ * hole.
|
|
|
+ */
|
|
|
+ goto hole;
|
|
|
+ case LCN_ENOMEM:
|
|
|
+ ntfs_error(vol->sb, "Not enough memory to complete "
|
|
|
+ "mapping for inode 0x%lx. "
|
|
|
+ "Returning 0.", ni->mft_no);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ntfs_error(vol->sb, "Failed to complete mapping for "
|
|
|
+ "inode 0x%lx. Run chkdsk. "
|
|
|
+ "Returning 0.", ni->mft_no);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (lcn < 0) {
|
|
|
+ /* It is a hole. */
|
|
|
+hole:
|
|
|
+ ntfs_debug("Done (returning hole).");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * The block is really allocated and fullfils all our criteria.
|
|
|
+ * Convert the cluster to units of block size and return the result.
|
|
|
+ */
|
|
|
+ delta = ofs & vol->cluster_size_mask;
|
|
|
+ if (unlikely(sizeof(block) < sizeof(lcn))) {
|
|
|
+ block = lcn = ((lcn << cluster_size_shift) + delta) >>
|
|
|
+ blocksize_bits;
|
|
|
+ /* If the block number was truncated return 0. */
|
|
|
+ if (unlikely(block != lcn)) {
|
|
|
+ ntfs_error(vol->sb, "Physical block 0x%llx is too "
|
|
|
+ "large to be returned, returning 0.",
|
|
|
+ (long long)lcn);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ block = ((lcn << cluster_size_shift) + delta) >>
|
|
|
+ blocksize_bits;
|
|
|
+ ntfs_debug("Done (returning block 0x%llx).", (unsigned long long)lcn);
|
|
|
+ return block;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ntfs_normal_aops - address space operations for normal inodes and attributes
|
|
|
*
|
|
@@ -1549,6 +1672,7 @@ const struct address_space_operations ntfs_normal_aops = {
|
|
|
.writepage = ntfs_writepage,
|
|
|
.set_page_dirty = __set_page_dirty_buffers,
|
|
|
#endif /* NTFS_RW */
|
|
|
+ .bmap = ntfs_bmap,
|
|
|
.migratepage = buffer_migrate_page,
|
|
|
.is_partially_uptodate = block_is_partially_uptodate,
|
|
|
.error_remove_page = generic_error_remove_page,
|