mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Description for this pull request:
- Replace the internal table lookup algorithm with the hweight library
     and ffs of the bitops library.
   - Handle the two types of stream entry, valid data size(has been written)
     and data size separately.It will improves compatibility with two
     differently sized files created on Windows.
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE6NzKS6Uv/XAAGHgyZwv7A1FEIQgFAmWgmxoWHGxpbmtpbmpl
 b25Aa2VybmVsLm9yZwAKCRBnC/sDUUQhCERgD/4rHm1yG0ZlURvXiAwZwVOQMJoz
 9Y8Gz3M1LsycJEN1uxNjSYfUe9LX/BlbXz5uIH8tVQjEEIbyl0RmJjITawVBHVbS
 Ps/UMDiQvT5DPqIwhrfTh9qxy0cRi7WBuKNAXRXSSVRx2mMWYIjNxT+8dIcD2FEG
 63ojnoYi8RwuYuvCwo51coxf8/E7GY+WAYnC97hqtj2jSQ6gMjeDtiyYx1m5PfUN
 32NdG1IYaiTstD7EU1lv1QNzLZx/Q9gBhi0jhDu1qc0fI+rS49p0zqop1TeEtsIf
 RD05XHZ8KRapChgoSvw+hb6CfZ7RanImFAHm6WnILqgFoY7uagUH1dn3oOJFgdLA
 OTwbEA/sQmnIdqg07Hhgf74OI9bu/kgP7g8/xrooqhO2SkYGLXDLgYFhEk08aEyE
 sp9fxtBfKhXUVHKafzkKtUmI+THl5W793aAfND5W+ahX2zDprwupzg/F7p4Sj3tJ
 GbvaRL/n1d/O1dhf/doTmfggH7TnDODS729w0HBSNJU+q6zrGluLRyqB3XsRFXng
 7RlN8f4HSI6eFRVG7KTwxVcfwsedtPmNRKLg3PEMkXz5jb4wsw7tZUB3gAFwy9qf
 cZd7/+oU9qKEgrBRDJfJsFqq0IpzLCXDEZp00F5RregLIhWHZN4ghBrVU94ciDuT
 gxBgoSWrLqObnXmLVQ==
 =WFoc
 -----END PGP SIGNATURE-----
Merge tag 'exfat-for-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
Pull exfat updates from Namjae Jeon:
 - Replace the internal table lookup algorithm with the hweight library
   and ffs of the bitops library.
 - Handle the two types of stream entry, valid data size (has been
   written) and data size separately. It improves compatibility with two
   differently sized files created on Windows.
* tag 'exfat-for-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: do not zero the extended part
  exfat: change to get file size from DataLength
  exfat: using ffs instead of internal logic
  exfat: using hweight instead of internal logic
			
			
This commit is contained in:
		
						commit
						052d534373
					
				
					 5 changed files with 335 additions and 92 deletions
				
			
		| 
						 | 
					@ -5,42 +5,23 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/blkdev.h>
 | 
					#include <linux/blkdev.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/bitmap.h>
 | 
				
			||||||
#include <linux/buffer_head.h>
 | 
					#include <linux/buffer_head.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "exfat_raw.h"
 | 
					#include "exfat_raw.h"
 | 
				
			||||||
#include "exfat_fs.h"
 | 
					#include "exfat_fs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const unsigned char free_bit[] = {
 | 
					#if BITS_PER_LONG == 32
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*  0 ~  19*/
 | 
					#define __le_long __le32
 | 
				
			||||||
	0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~  39*/
 | 
					#define lel_to_cpu(A) le32_to_cpu(A)
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~  59*/
 | 
					#define cpu_to_lel(A) cpu_to_le32(A)
 | 
				
			||||||
	0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~  79*/
 | 
					#elif BITS_PER_LONG == 64
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~  99*/
 | 
					#define __le_long __le64
 | 
				
			||||||
	0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
 | 
					#define lel_to_cpu(A) le64_to_cpu(A)
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
 | 
					#define cpu_to_lel(A) cpu_to_le64(A)
 | 
				
			||||||
	0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
 | 
					#else
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
 | 
					#error "BITS_PER_LONG not 32 or 64"
 | 
				
			||||||
	0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
 | 
					#endif
 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/
 | 
					 | 
				
			||||||
	0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/
 | 
					 | 
				
			||||||
	0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0                /*240 ~ 254*/
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const unsigned char used_bit[] = {
 | 
					 | 
				
			||||||
	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/*  0 ~  19*/
 | 
					 | 
				
			||||||
	2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~  39*/
 | 
					 | 
				
			||||||
	2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~  59*/
 | 
					 | 
				
			||||||
	4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~  79*/
 | 
					 | 
				
			||||||
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~  99*/
 | 
					 | 
				
			||||||
	3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
 | 
					 | 
				
			||||||
	4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
 | 
					 | 
				
			||||||
	3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
 | 
					 | 
				
			||||||
	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
 | 
					 | 
				
			||||||
	4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
 | 
					 | 
				
			||||||
	3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
 | 
					 | 
				
			||||||
	5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
 | 
					 | 
				
			||||||
	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8             /*240 ~ 255*/
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *  Allocation Bitmap Management Functions
 | 
					 *  Allocation Bitmap Management Functions
 | 
				
			||||||
| 
						 | 
					@ -200,32 +181,35 @@ unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int i, map_i, map_b, ent_idx;
 | 
						unsigned int i, map_i, map_b, ent_idx;
 | 
				
			||||||
	unsigned int clu_base, clu_free;
 | 
						unsigned int clu_base, clu_free;
 | 
				
			||||||
	unsigned char k, clu_mask;
 | 
						unsigned long clu_bits, clu_mask;
 | 
				
			||||||
	struct exfat_sb_info *sbi = EXFAT_SB(sb);
 | 
						struct exfat_sb_info *sbi = EXFAT_SB(sb);
 | 
				
			||||||
 | 
						__le_long bitval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN_ON(clu < EXFAT_FIRST_CLUSTER);
 | 
						WARN_ON(clu < EXFAT_FIRST_CLUSTER);
 | 
				
			||||||
	ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
 | 
						ent_idx = ALIGN_DOWN(CLUSTER_TO_BITMAP_ENT(clu), BITS_PER_LONG);
 | 
				
			||||||
	clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK));
 | 
						clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx);
 | 
				
			||||||
	clu_mask = IGNORED_BITS_REMAINED(clu, clu_base);
 | 
						clu_mask = IGNORED_BITS_REMAINED(clu, clu_base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
 | 
						map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
 | 
				
			||||||
	map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx);
 | 
						map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters;
 | 
						for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters;
 | 
				
			||||||
	     i += BITS_PER_BYTE) {
 | 
						     i += BITS_PER_LONG) {
 | 
				
			||||||
		k = *(sbi->vol_amap[map_i]->b_data + map_b);
 | 
							bitval = *(__le_long *)(sbi->vol_amap[map_i]->b_data + map_b);
 | 
				
			||||||
		if (clu_mask > 0) {
 | 
							if (clu_mask > 0) {
 | 
				
			||||||
			k |= clu_mask;
 | 
								bitval |= cpu_to_lel(clu_mask);
 | 
				
			||||||
			clu_mask = 0;
 | 
								clu_mask = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (k < 0xFF) {
 | 
							if (lel_to_cpu(bitval) != ULONG_MAX) {
 | 
				
			||||||
			clu_free = clu_base + free_bit[k];
 | 
								clu_bits = lel_to_cpu(bitval);
 | 
				
			||||||
 | 
								clu_free = clu_base + ffz(clu_bits);
 | 
				
			||||||
			if (clu_free < sbi->num_clusters)
 | 
								if (clu_free < sbi->num_clusters)
 | 
				
			||||||
				return clu_free;
 | 
									return clu_free;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		clu_base += BITS_PER_BYTE;
 | 
							clu_base += BITS_PER_LONG;
 | 
				
			||||||
 | 
							map_b += sizeof(long);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (++map_b >= sb->s_blocksize ||
 | 
							if (map_b >= sb->s_blocksize ||
 | 
				
			||||||
		    clu_base >= sbi->num_clusters) {
 | 
							    clu_base >= sbi->num_clusters) {
 | 
				
			||||||
			if (++map_i >= sbi->map_sectors) {
 | 
								if (++map_i >= sbi->map_sectors) {
 | 
				
			||||||
				clu_base = EXFAT_FIRST_CLUSTER;
 | 
									clu_base = EXFAT_FIRST_CLUSTER;
 | 
				
			||||||
| 
						 | 
					@ -244,25 +228,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
 | 
				
			||||||
	unsigned int count = 0;
 | 
						unsigned int count = 0;
 | 
				
			||||||
	unsigned int i, map_i = 0, map_b = 0;
 | 
						unsigned int i, map_i = 0, map_b = 0;
 | 
				
			||||||
	unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
 | 
						unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
 | 
				
			||||||
	unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK;
 | 
						unsigned int last_mask = total_clus & (BITS_PER_LONG - 1);
 | 
				
			||||||
	unsigned char clu_bits;
 | 
						unsigned long *bitmap, clu_bits;
 | 
				
			||||||
	const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
 | 
					 | 
				
			||||||
		0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	total_clus &= ~last_mask;
 | 
						total_clus &= ~last_mask;
 | 
				
			||||||
	for (i = 0; i < total_clus; i += BITS_PER_BYTE) {
 | 
						for (i = 0; i < total_clus; i += BITS_PER_LONG) {
 | 
				
			||||||
		clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
 | 
							bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
 | 
				
			||||||
		count += used_bit[clu_bits];
 | 
							count += hweight_long(*bitmap);
 | 
				
			||||||
		if (++map_b >= (unsigned int)sb->s_blocksize) {
 | 
							map_b += sizeof(long);
 | 
				
			||||||
 | 
							if (map_b >= (unsigned int)sb->s_blocksize) {
 | 
				
			||||||
			map_i++;
 | 
								map_i++;
 | 
				
			||||||
			map_b = 0;
 | 
								map_b = 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (last_mask) {
 | 
						if (last_mask) {
 | 
				
			||||||
		clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
 | 
							bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
 | 
				
			||||||
		clu_bits &= last_bit_mask[last_mask];
 | 
							clu_bits = lel_to_cpu(*(__le_long *)bitmap);
 | 
				
			||||||
		count += used_bit[clu_bits];
 | 
							count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*ret_count = count;
 | 
						*ret_count = count;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,8 +135,7 @@ enum {
 | 
				
			||||||
#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb))
 | 
					#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb))
 | 
				
			||||||
#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \
 | 
					#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \
 | 
				
			||||||
	((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1))
 | 
						((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1))
 | 
				
			||||||
#define BITS_PER_BYTE_MASK	0x7
 | 
					#define IGNORED_BITS_REMAINED(clu, clu_base) ((1UL << ((clu) - (clu_base))) - 1)
 | 
				
			||||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ES_ENTRY_NUM(name_len)	(ES_IDX_LAST_FILENAME(name_len) + 1)
 | 
					#define ES_ENTRY_NUM(name_len)	(ES_IDX_LAST_FILENAME(name_len) + 1)
 | 
				
			||||||
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
 | 
					/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
 | 
				
			||||||
| 
						 | 
					@ -208,6 +207,7 @@ struct exfat_dir_entry {
 | 
				
			||||||
	unsigned char flags;
 | 
						unsigned char flags;
 | 
				
			||||||
	unsigned short attr;
 | 
						unsigned short attr;
 | 
				
			||||||
	loff_t size;
 | 
						loff_t size;
 | 
				
			||||||
 | 
						loff_t valid_size;
 | 
				
			||||||
	unsigned int num_subdirs;
 | 
						unsigned int num_subdirs;
 | 
				
			||||||
	struct timespec64 atime;
 | 
						struct timespec64 atime;
 | 
				
			||||||
	struct timespec64 mtime;
 | 
						struct timespec64 mtime;
 | 
				
			||||||
| 
						 | 
					@ -317,6 +317,7 @@ struct exfat_inode_info {
 | 
				
			||||||
	loff_t i_size_aligned;
 | 
						loff_t i_size_aligned;
 | 
				
			||||||
	/* on-disk position of directory entry or 0 */
 | 
						/* on-disk position of directory entry or 0 */
 | 
				
			||||||
	loff_t i_pos;
 | 
						loff_t i_pos;
 | 
				
			||||||
 | 
						loff_t valid_size;
 | 
				
			||||||
	/* hash by i_location */
 | 
						/* hash by i_location */
 | 
				
			||||||
	struct hlist_node i_hash_fat;
 | 
						struct hlist_node i_hash_fat;
 | 
				
			||||||
	/* protect bmap against truncate */
 | 
						/* protect bmap against truncate */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										191
									
								
								fs/exfat/file.c
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								fs/exfat/file.c
									
									
									
									
									
								
							| 
						 | 
					@ -11,37 +11,76 @@
 | 
				
			||||||
#include <linux/fsnotify.h>
 | 
					#include <linux/fsnotify.h>
 | 
				
			||||||
#include <linux/security.h>
 | 
					#include <linux/security.h>
 | 
				
			||||||
#include <linux/msdos_fs.h>
 | 
					#include <linux/msdos_fs.h>
 | 
				
			||||||
 | 
					#include <linux/writeback.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "exfat_raw.h"
 | 
					#include "exfat_raw.h"
 | 
				
			||||||
#include "exfat_fs.h"
 | 
					#include "exfat_fs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int exfat_cont_expand(struct inode *inode, loff_t size)
 | 
					static int exfat_cont_expand(struct inode *inode, loff_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct address_space *mapping = inode->i_mapping;
 | 
						int ret;
 | 
				
			||||||
	loff_t start = i_size_read(inode), count = size - i_size_read(inode);
 | 
						unsigned int num_clusters, new_num_clusters, last_clu;
 | 
				
			||||||
	int err, err2;
 | 
						struct exfat_inode_info *ei = EXFAT_I(inode);
 | 
				
			||||||
 | 
						struct super_block *sb = inode->i_sb;
 | 
				
			||||||
 | 
						struct exfat_sb_info *sbi = EXFAT_SB(sb);
 | 
				
			||||||
 | 
						struct exfat_chain clu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = generic_cont_expand_simple(inode, size);
 | 
						ret = inode_newsize_ok(inode, size);
 | 
				
			||||||
	if (err)
 | 
						if (ret)
 | 
				
			||||||
		return err;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
 | 
				
			||||||
 | 
						new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (new_num_clusters == num_clusters)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exfat_chain_set(&clu, ei->start_clu, num_clusters, ei->flags);
 | 
				
			||||||
 | 
						ret = exfat_find_last_cluster(sb, &clu, &last_clu);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ?
 | 
				
			||||||
 | 
								EXFAT_EOF_CLUSTER : last_clu + 1;
 | 
				
			||||||
 | 
						clu.size = 0;
 | 
				
			||||||
 | 
						clu.flags = ei->flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = exfat_alloc_cluster(inode, new_num_clusters - num_clusters,
 | 
				
			||||||
 | 
								&clu, IS_DIRSYNC(inode));
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Append new clusters to chain */
 | 
				
			||||||
 | 
						if (clu.flags != ei->flags) {
 | 
				
			||||||
 | 
							exfat_chain_cont_cluster(sb, ei->start_clu, num_clusters);
 | 
				
			||||||
 | 
							ei->flags = ALLOC_FAT_CHAIN;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (clu.flags == ALLOC_FAT_CHAIN)
 | 
				
			||||||
 | 
							if (exfat_ent_set(sb, last_clu, clu.dir))
 | 
				
			||||||
 | 
								goto free_clu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (num_clusters == 0)
 | 
				
			||||||
 | 
							ei->start_clu = clu.dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 | 
						inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 | 
				
			||||||
 | 
						/* Expanded range not zeroed, do not update valid_size */
 | 
				
			||||||
 | 
						i_size_write(inode, size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ei->i_size_aligned = round_up(size, sb->s_blocksize);
 | 
				
			||||||
 | 
						ei->i_size_ondisk = ei->i_size_aligned;
 | 
				
			||||||
 | 
						inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (IS_DIRSYNC(inode))
 | 
				
			||||||
 | 
							return write_inode_now(inode, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mark_inode_dirty(inode);
 | 
						mark_inode_dirty(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!IS_SYNC(inode))
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = filemap_fdatawrite_range(mapping, start, start + count - 1);
 | 
					free_clu:
 | 
				
			||||||
	err2 = sync_mapping_buffers(mapping);
 | 
						exfat_free_cluster(inode, &clu);
 | 
				
			||||||
	if (!err)
 | 
						return -EIO;
 | 
				
			||||||
		err = err2;
 | 
					 | 
				
			||||||
	err2 = write_inode_now(inode, 1);
 | 
					 | 
				
			||||||
	if (!err)
 | 
					 | 
				
			||||||
		err = err2;
 | 
					 | 
				
			||||||
	if (err)
 | 
					 | 
				
			||||||
		return err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filemap_fdatawait_range(mapping, start, start + count - 1);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
 | 
					static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
 | 
				
			||||||
| 
						 | 
					@ -146,6 +185,9 @@ int __exfat_truncate(struct inode *inode)
 | 
				
			||||||
		ei->start_clu = EXFAT_EOF_CLUSTER;
 | 
							ei->start_clu = EXFAT_EOF_CLUSTER;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (i_size_read(inode) < ei->valid_size)
 | 
				
			||||||
 | 
							ei->valid_size = i_size_read(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ei->type == TYPE_FILE)
 | 
						if (ei->type == TYPE_FILE)
 | 
				
			||||||
		ei->attr |= EXFAT_ATTR_ARCHIVE;
 | 
							ei->attr |= EXFAT_ATTR_ARCHIVE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -474,15 +516,124 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
 | 
				
			||||||
	return blkdev_issue_flush(inode->i_sb->s_bdev);
 | 
						return blkdev_issue_flush(inode->i_sb->s_bdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
						struct inode *inode = file_inode(file);
 | 
				
			||||||
 | 
						struct address_space *mapping = inode->i_mapping;
 | 
				
			||||||
 | 
						const struct address_space_operations *ops = mapping->a_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (start < end) {
 | 
				
			||||||
 | 
							u32 zerofrom, len;
 | 
				
			||||||
 | 
							struct page *page = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							zerofrom = start & (PAGE_SIZE - 1);
 | 
				
			||||||
 | 
							len = PAGE_SIZE - zerofrom;
 | 
				
			||||||
 | 
							if (start + len > end)
 | 
				
			||||||
 | 
								len = end - start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = ops->write_begin(file, mapping, start, len, &page, NULL);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							zero_user_segment(page, zerofrom, zerofrom + len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = ops->write_end(file, mapping, start, len, len, page, NULL);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							start += len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							balance_dirty_pages_ratelimited(mapping);
 | 
				
			||||||
 | 
							cond_resched();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t ret;
 | 
				
			||||||
 | 
						struct file *file = iocb->ki_filp;
 | 
				
			||||||
 | 
						struct inode *inode = file_inode(file);
 | 
				
			||||||
 | 
						struct exfat_inode_info *ei = EXFAT_I(inode);
 | 
				
			||||||
 | 
						loff_t pos = iocb->ki_pos;
 | 
				
			||||||
 | 
						loff_t valid_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inode_lock(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						valid_size = ei->valid_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = generic_write_checks(iocb, iter);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							goto unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pos > valid_size) {
 | 
				
			||||||
 | 
							ret = exfat_file_zeroed_range(file, valid_size, pos);
 | 
				
			||||||
 | 
							if (ret < 0 && ret != -ENOSPC) {
 | 
				
			||||||
 | 
								exfat_err(inode->i_sb,
 | 
				
			||||||
 | 
									"write: fail to zero from %llu to %llu(%zd)",
 | 
				
			||||||
 | 
									valid_size, pos, ret);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (ret < 0)
 | 
				
			||||||
 | 
								goto unlock;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = __generic_file_write_iter(iocb, iter);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							goto unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inode_unlock(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pos > valid_size)
 | 
				
			||||||
 | 
							pos = valid_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (iocb_is_dsync(iocb) && iocb->ki_pos > pos) {
 | 
				
			||||||
 | 
							ssize_t err = vfs_fsync_range(file, pos, iocb->ki_pos - 1,
 | 
				
			||||||
 | 
									iocb->ki_flags & IOCB_SYNC);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock:
 | 
				
			||||||
 | 
						inode_unlock(inode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						struct inode *inode = file_inode(file);
 | 
				
			||||||
 | 
						struct exfat_inode_info *ei = EXFAT_I(inode);
 | 
				
			||||||
 | 
						loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
 | 
				
			||||||
 | 
						loff_t end = min_t(loff_t, i_size_read(inode),
 | 
				
			||||||
 | 
								start + vma->vm_end - vma->vm_start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
 | 
				
			||||||
 | 
							ret = exfat_file_zeroed_range(file, ei->valid_size, end);
 | 
				
			||||||
 | 
							if (ret < 0) {
 | 
				
			||||||
 | 
								exfat_err(inode->i_sb,
 | 
				
			||||||
 | 
									  "mmap: fail to zero from %llu to %llu(%d)",
 | 
				
			||||||
 | 
									  start, end, ret);
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return generic_file_mmap(file, vma);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct file_operations exfat_file_operations = {
 | 
					const struct file_operations exfat_file_operations = {
 | 
				
			||||||
	.llseek		= generic_file_llseek,
 | 
						.llseek		= generic_file_llseek,
 | 
				
			||||||
	.read_iter	= generic_file_read_iter,
 | 
						.read_iter	= generic_file_read_iter,
 | 
				
			||||||
	.write_iter	= generic_file_write_iter,
 | 
						.write_iter	= exfat_file_write_iter,
 | 
				
			||||||
	.unlocked_ioctl = exfat_ioctl,
 | 
						.unlocked_ioctl = exfat_ioctl,
 | 
				
			||||||
#ifdef CONFIG_COMPAT
 | 
					#ifdef CONFIG_COMPAT
 | 
				
			||||||
	.compat_ioctl = exfat_compat_ioctl,
 | 
						.compat_ioctl = exfat_compat_ioctl,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	.mmap		= generic_file_mmap,
 | 
						.mmap		= exfat_file_mmap,
 | 
				
			||||||
	.fsync		= exfat_file_fsync,
 | 
						.fsync		= exfat_file_fsync,
 | 
				
			||||||
	.splice_read	= filemap_splice_read,
 | 
						.splice_read	= filemap_splice_read,
 | 
				
			||||||
	.splice_write	= iter_file_splice_write,
 | 
						.splice_write	= iter_file_splice_write,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										132
									
								
								fs/exfat/inode.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								fs/exfat/inode.c
									
									
									
									
									
								
							| 
						 | 
					@ -75,8 +75,17 @@ int __exfat_write_inode(struct inode *inode, int sync)
 | 
				
			||||||
	if (ei->start_clu == EXFAT_EOF_CLUSTER)
 | 
						if (ei->start_clu == EXFAT_EOF_CLUSTER)
 | 
				
			||||||
		on_disk_size = 0;
 | 
							on_disk_size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size);
 | 
						ep2->dentry.stream.size = cpu_to_le64(on_disk_size);
 | 
				
			||||||
	ep2->dentry.stream.size = ep2->dentry.stream.valid_size;
 | 
						/*
 | 
				
			||||||
 | 
						 * mmap write does not use exfat_write_end(), valid_size may be
 | 
				
			||||||
 | 
						 * extended to the sector-aligned length in exfat_get_block().
 | 
				
			||||||
 | 
						 * So we need to fixup valid_size to the writren length.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (on_disk_size < ei->valid_size)
 | 
				
			||||||
 | 
							ep2->dentry.stream.valid_size = ep2->dentry.stream.size;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (on_disk_size) {
 | 
						if (on_disk_size) {
 | 
				
			||||||
		ep2->dentry.stream.flags = ei->flags;
 | 
							ep2->dentry.stream.flags = ei->flags;
 | 
				
			||||||
		ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
 | 
							ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu);
 | 
				
			||||||
| 
						 | 
					@ -278,6 +287,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
 | 
				
			||||||
	unsigned int cluster, sec_offset;
 | 
						unsigned int cluster, sec_offset;
 | 
				
			||||||
	sector_t last_block;
 | 
						sector_t last_block;
 | 
				
			||||||
	sector_t phys = 0;
 | 
						sector_t phys = 0;
 | 
				
			||||||
 | 
						sector_t valid_blks;
 | 
				
			||||||
	loff_t pos;
 | 
						loff_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&sbi->s_lock);
 | 
						mutex_lock(&sbi->s_lock);
 | 
				
			||||||
| 
						 | 
					@ -306,17 +316,32 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
 | 
				
			||||||
	mapped_blocks = sbi->sect_per_clus - sec_offset;
 | 
						mapped_blocks = sbi->sect_per_clus - sec_offset;
 | 
				
			||||||
	max_blocks = min(mapped_blocks, max_blocks);
 | 
						max_blocks = min(mapped_blocks, max_blocks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Treat newly added block / cluster */
 | 
					 | 
				
			||||||
	if (iblock < last_block)
 | 
					 | 
				
			||||||
		create = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (create || buffer_delay(bh_result)) {
 | 
					 | 
				
			||||||
	pos = EXFAT_BLK_TO_B((iblock + 1), sb);
 | 
						pos = EXFAT_BLK_TO_B((iblock + 1), sb);
 | 
				
			||||||
 | 
						if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
 | 
				
			||||||
		if (ei->i_size_ondisk < pos)
 | 
							if (ei->i_size_ondisk < pos)
 | 
				
			||||||
			ei->i_size_ondisk = pos;
 | 
								ei->i_size_ondisk = pos;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map_bh(bh_result, sb, phys);
 | 
				
			||||||
 | 
						if (buffer_delay(bh_result))
 | 
				
			||||||
 | 
							clear_buffer_delay(bh_result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (create) {
 | 
						if (create) {
 | 
				
			||||||
 | 
							valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (iblock + max_blocks < valid_blks) {
 | 
				
			||||||
 | 
								/* The range has been written, map it */
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							} else if (iblock < valid_blks) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * The range has been partially written,
 | 
				
			||||||
 | 
								 * map the written part.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								max_blocks = valid_blks - iblock;
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* The area has not been written, map and mark as new. */
 | 
				
			||||||
		err = exfat_map_new_buffer(ei, bh_result, pos);
 | 
							err = exfat_map_new_buffer(ei, bh_result, pos);
 | 
				
			||||||
		if (err) {
 | 
							if (err) {
 | 
				
			||||||
			exfat_fs_error(sb,
 | 
								exfat_fs_error(sb,
 | 
				
			||||||
| 
						 | 
					@ -324,11 +349,58 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
 | 
				
			||||||
					pos, ei->i_size_aligned);
 | 
										pos, ei->i_size_aligned);
 | 
				
			||||||
			goto unlock_ret;
 | 
								goto unlock_ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buffer_delay(bh_result))
 | 
							ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
 | 
				
			||||||
		clear_buffer_delay(bh_result);
 | 
							mark_inode_dirty(inode);
 | 
				
			||||||
	map_bh(bh_result, sb, phys);
 | 
						} else {
 | 
				
			||||||
 | 
							valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (iblock + max_blocks < valid_blks) {
 | 
				
			||||||
 | 
								/* The range has been written, map it */
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							} else if (iblock < valid_blks) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * The area has been partially written,
 | 
				
			||||||
 | 
								 * map the written part.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								max_blocks = valid_blks - iblock;
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							} else if (iblock == valid_blks &&
 | 
				
			||||||
 | 
								   (ei->valid_size & (sb->s_blocksize - 1))) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * The block has been partially written,
 | 
				
			||||||
 | 
								 * zero the unwritten part and map the block.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								loff_t size, off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								max_blocks = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * For direct read, the unwritten part will be zeroed in
 | 
				
			||||||
 | 
								 * exfat_direct_IO()
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (!bh_result->b_folio)
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								pos -= sb->s_blocksize;
 | 
				
			||||||
 | 
								size = ei->valid_size - pos;
 | 
				
			||||||
 | 
								off = pos & (PAGE_SIZE - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								folio_set_bh(bh_result, bh_result->b_folio, off);
 | 
				
			||||||
 | 
								err = bh_read(bh_result, 0);
 | 
				
			||||||
 | 
								if (err < 0)
 | 
				
			||||||
 | 
									goto unlock_ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								folio_zero_segment(bh_result->b_folio, off + size,
 | 
				
			||||||
 | 
										off + sb->s_blocksize);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * The range has not been written, clear the mapped flag
 | 
				
			||||||
 | 
								 * to only zero the cache and do not read from disk.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								clear_buffer_mapped(bh_result);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
	bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb);
 | 
						bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb);
 | 
				
			||||||
unlock_ret:
 | 
					unlock_ret:
 | 
				
			||||||
| 
						 | 
					@ -343,6 +415,17 @@ static int exfat_read_folio(struct file *file, struct folio *folio)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void exfat_readahead(struct readahead_control *rac)
 | 
					static void exfat_readahead(struct readahead_control *rac)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct address_space *mapping = rac->mapping;
 | 
				
			||||||
 | 
						struct inode *inode = mapping->host;
 | 
				
			||||||
 | 
						struct exfat_inode_info *ei = EXFAT_I(inode);
 | 
				
			||||||
 | 
						loff_t pos = readahead_pos(rac);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Range cross valid_size, read it page by page. */
 | 
				
			||||||
 | 
						if (ei->valid_size < i_size_read(inode) &&
 | 
				
			||||||
 | 
						    pos <= ei->valid_size &&
 | 
				
			||||||
 | 
						    ei->valid_size < pos + readahead_length(rac))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mpage_readahead(rac, exfat_get_block);
 | 
						mpage_readahead(rac, exfat_get_block);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,9 +453,7 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*pagep = NULL;
 | 
						*pagep = NULL;
 | 
				
			||||||
	ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
 | 
						ret = block_write_begin(mapping, pos, len, pagep, exfat_get_block);
 | 
				
			||||||
			       exfat_get_block,
 | 
					 | 
				
			||||||
			       &EXFAT_I(mapping->host)->i_size_ondisk);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		exfat_write_failed(mapping, pos+len);
 | 
							exfat_write_failed(mapping, pos+len);
 | 
				
			||||||
| 
						 | 
					@ -400,6 +481,11 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
 | 
				
			||||||
	if (err < len)
 | 
						if (err < len)
 | 
				
			||||||
		exfat_write_failed(mapping, pos+len);
 | 
							exfat_write_failed(mapping, pos+len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(err < 0) && pos + err > ei->valid_size) {
 | 
				
			||||||
 | 
							ei->valid_size = pos + err;
 | 
				
			||||||
 | 
							mark_inode_dirty(inode);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
 | 
						if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
 | 
				
			||||||
		inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 | 
							inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
 | 
				
			||||||
		ei->attr |= EXFAT_ATTR_ARCHIVE;
 | 
							ei->attr |= EXFAT_ATTR_ARCHIVE;
 | 
				
			||||||
| 
						 | 
					@ -413,6 +499,8 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct address_space *mapping = iocb->ki_filp->f_mapping;
 | 
						struct address_space *mapping = iocb->ki_filp->f_mapping;
 | 
				
			||||||
	struct inode *inode = mapping->host;
 | 
						struct inode *inode = mapping->host;
 | 
				
			||||||
 | 
						struct exfat_inode_info *ei = EXFAT_I(inode);
 | 
				
			||||||
 | 
						loff_t pos = iocb->ki_pos;
 | 
				
			||||||
	loff_t size = iocb->ki_pos + iov_iter_count(iter);
 | 
						loff_t size = iocb->ki_pos + iov_iter_count(iter);
 | 
				
			||||||
	int rw = iov_iter_rw(iter);
 | 
						int rw = iov_iter_rw(iter);
 | 
				
			||||||
	ssize_t ret;
 | 
						ssize_t ret;
 | 
				
			||||||
| 
						 | 
					@ -436,8 +524,21 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
 | 
				
			||||||
	 * condition of exfat_get_block() and ->truncate().
 | 
						 * condition of exfat_get_block() and ->truncate().
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
 | 
						ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block);
 | 
				
			||||||
	if (ret < 0 && (rw & WRITE))
 | 
						if (ret < 0) {
 | 
				
			||||||
 | 
							if (rw == WRITE)
 | 
				
			||||||
			exfat_write_failed(mapping, size);
 | 
								exfat_write_failed(mapping, size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (ret != -EIOCBQUEUED)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							size = pos + ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* zero the unwritten part in the partially written block */
 | 
				
			||||||
 | 
						if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
 | 
				
			||||||
 | 
							iov_iter_revert(iter, size - ei->valid_size);
 | 
				
			||||||
 | 
							iov_iter_zero(size - ei->valid_size, iter);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -537,6 +638,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
 | 
				
			||||||
	ei->start_clu = info->start_clu;
 | 
						ei->start_clu = info->start_clu;
 | 
				
			||||||
	ei->flags = info->flags;
 | 
						ei->flags = info->flags;
 | 
				
			||||||
	ei->type = info->type;
 | 
						ei->type = info->type;
 | 
				
			||||||
 | 
						ei->valid_size = info->valid_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ei->version = 0;
 | 
						ei->version = 0;
 | 
				
			||||||
	ei->hint_stat.eidx = 0;
 | 
						ei->hint_stat.eidx = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -406,6 +406,7 @@ static int exfat_find_empty_entry(struct inode *inode,
 | 
				
			||||||
		i_size_write(inode, size);
 | 
							i_size_write(inode, size);
 | 
				
			||||||
		ei->i_size_ondisk += sbi->cluster_size;
 | 
							ei->i_size_ondisk += sbi->cluster_size;
 | 
				
			||||||
		ei->i_size_aligned += sbi->cluster_size;
 | 
							ei->i_size_aligned += sbi->cluster_size;
 | 
				
			||||||
 | 
							ei->valid_size += sbi->cluster_size;
 | 
				
			||||||
		ei->flags = p_dir->flags;
 | 
							ei->flags = p_dir->flags;
 | 
				
			||||||
		inode->i_blocks += sbi->cluster_size >> 9;
 | 
							inode->i_blocks += sbi->cluster_size >> 9;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -558,6 +559,8 @@ static int exfat_add_entry(struct inode *inode, const char *path,
 | 
				
			||||||
		info->size = clu_size;
 | 
							info->size = clu_size;
 | 
				
			||||||
		info->num_subdirs = EXFAT_MIN_SUBDIR;
 | 
							info->num_subdirs = EXFAT_MIN_SUBDIR;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						info->valid_size = info->size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(&info->crtime, 0, sizeof(info->crtime));
 | 
						memset(&info->crtime, 0, sizeof(info->crtime));
 | 
				
			||||||
	memset(&info->mtime, 0, sizeof(info->mtime));
 | 
						memset(&info->mtime, 0, sizeof(info->mtime));
 | 
				
			||||||
	memset(&info->atime, 0, sizeof(info->atime));
 | 
						memset(&info->atime, 0, sizeof(info->atime));
 | 
				
			||||||
| 
						 | 
					@ -660,6 +663,8 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
 | 
				
			||||||
	info->type = exfat_get_entry_type(ep);
 | 
						info->type = exfat_get_entry_type(ep);
 | 
				
			||||||
	info->attr = le16_to_cpu(ep->dentry.file.attr);
 | 
						info->attr = le16_to_cpu(ep->dentry.file.attr);
 | 
				
			||||||
	info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
 | 
						info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
 | 
				
			||||||
 | 
						info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size);
 | 
				
			||||||
 | 
						info->size = le64_to_cpu(ep2->dentry.stream.size);
 | 
				
			||||||
	if (info->size == 0) {
 | 
						if (info->size == 0) {
 | 
				
			||||||
		info->flags = ALLOC_NO_FAT_CHAIN;
 | 
							info->flags = ALLOC_NO_FAT_CHAIN;
 | 
				
			||||||
		info->start_clu = EXFAT_EOF_CLUSTER;
 | 
							info->start_clu = EXFAT_EOF_CLUSTER;
 | 
				
			||||||
| 
						 | 
					@ -1288,6 +1293,7 @@ static int __exfat_rename(struct inode *old_parent_inode,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			i_size_write(new_inode, 0);
 | 
								i_size_write(new_inode, 0);
 | 
				
			||||||
 | 
								new_ei->valid_size = 0;
 | 
				
			||||||
			new_ei->start_clu = EXFAT_EOF_CLUSTER;
 | 
								new_ei->start_clu = EXFAT_EOF_CLUSTER;
 | 
				
			||||||
			new_ei->flags = ALLOC_NO_FAT_CHAIN;
 | 
								new_ei->flags = ALLOC_NO_FAT_CHAIN;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue