Merge branch 'for-kernel-version-from-4.1.0' of https://github.com/namjaejeon/linux-exfat-oot into android13-5.4-lahaina
* 'for-kernel-version-from-4.1.0' of https://github.com/namjaejeon/linux-exfat-oot: exfat: github action: run buiuld and tests on for-kernel-version-from-4.1.0 branch exfat: fix ALIGN_DOWN undefined error exfat: using ffs instead of internal logic exfat: using hweight instead of internal logic exfat: fix ctime is not updated exfat: fix setting uninitialized time to ctime/atime exfat: convert to new timestamp accessors exfat: convert to ctime accessor functions exfat: fs: pass the request_mask to generic_fillattr exfat: convert to simple_rename_timestamp exfat: ensure that ctime is updated whenever the mtime is exfat: fs: add CONFIG_BUFFER_HEAD exfat: use fat ioctls definitions from include/uapi/linux/msdos_fs.h exfat: github action: remove liunx-4.1 source to get more disk space exfat: support create zero-size directory exfat: support handle zero-size directory exfat: add ioctls for accessing attributes exfat: vfs: get rid of old '->iterate' directory operation Change-Id: I7e8add708697faf95952cacdb24bf5f504190fe4
This commit is contained in:
commit
d561bf243b
9
fs/exfat/.github/workflows/c-cpp.yml
vendored
9
fs/exfat/.github/workflows/c-cpp.yml
vendored
@ -2,9 +2,9 @@ name: linux-exfat-oot CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "for-kernel-version-from-4.1.0" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "for-kernel-version-from-4.1.0" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -20,6 +20,7 @@ jobs:
|
||||
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.1.36.tar.gz
|
||||
tar xf linux-4.1.36.tar.gz
|
||||
mv linux-4.1.36 linux-stable
|
||||
rm -rf linux-4.1.36.tar.gz
|
||||
- name: Prerequisite for xfstests testing
|
||||
run: |
|
||||
sudo apt-get install linux-headers-$(uname -r)
|
||||
@ -47,7 +48,9 @@ jobs:
|
||||
make -j$((`nproc`+1)) fs/exfat/exfat.ko
|
||||
- name: Run xfstests testsuite
|
||||
run: |
|
||||
cd ../linux-exfat-oot
|
||||
cd ..
|
||||
rm -rf linux-stable
|
||||
cd linux-exfat-oot
|
||||
make > /dev/null
|
||||
sudo make install > /dev/null
|
||||
sudo insmod exfat.ko
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
config EXFAT_FS
|
||||
tristate "exFAT filesystem support"
|
||||
select BUFFER_HEAD
|
||||
select NLS
|
||||
select LEGACY_DIRECT_IO
|
||||
help
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/sched/signal.h>
|
||||
@ -17,37 +18,21 @@
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
||||
static const unsigned char free_bit[] = {
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
|
||||
0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/
|
||||
0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
|
||||
0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
|
||||
0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
|
||||
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*/
|
||||
};
|
||||
#if BITS_PER_LONG == 32
|
||||
#define __le_long __le32
|
||||
#define lel_to_cpu(A) le32_to_cpu(A)
|
||||
#define cpu_to_lel(A) cpu_to_le32(A)
|
||||
#elif BITS_PER_LONG == 64
|
||||
#define __le_long __le64
|
||||
#define lel_to_cpu(A) le64_to_cpu(A)
|
||||
#define cpu_to_lel(A) cpu_to_le64(A)
|
||||
#else
|
||||
#error "BITS_PER_LONG not 32 or 64"
|
||||
#endif
|
||||
|
||||
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*/
|
||||
};
|
||||
#ifndef ALIGN_DOWN
|
||||
#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocation Bitmap Management Functions
|
||||
@ -211,32 +196,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 clu_base, clu_free;
|
||||
unsigned char k, clu_mask;
|
||||
unsigned long clu_bits, clu_mask;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
__le_long bitval;
|
||||
|
||||
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
|
||||
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK));
|
||||
ent_idx = ALIGN_DOWN(CLUSTER_TO_BITMAP_ENT(clu), BITS_PER_LONG);
|
||||
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx);
|
||||
clu_mask = IGNORED_BITS_REMAINED(clu, clu_base);
|
||||
|
||||
map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters;
|
||||
i += BITS_PER_BYTE) {
|
||||
k = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
i += BITS_PER_LONG) {
|
||||
bitval = *(__le_long *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
if (clu_mask > 0) {
|
||||
k |= clu_mask;
|
||||
bitval |= cpu_to_lel(clu_mask);
|
||||
clu_mask = 0;
|
||||
}
|
||||
if (k < 0xFF) {
|
||||
clu_free = clu_base + free_bit[k];
|
||||
if (lel_to_cpu(bitval) != ULONG_MAX) {
|
||||
clu_bits = lel_to_cpu(bitval);
|
||||
clu_free = clu_base + ffz(clu_bits);
|
||||
if (clu_free < sbi->num_clusters)
|
||||
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) {
|
||||
if (++map_i >= sbi->map_sectors) {
|
||||
clu_base = EXFAT_FIRST_CLUSTER;
|
||||
@ -255,25 +243,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
|
||||
unsigned int count = 0;
|
||||
unsigned int i, map_i = 0, map_b = 0;
|
||||
unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
|
||||
unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK;
|
||||
unsigned char clu_bits;
|
||||
const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
|
||||
0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
|
||||
unsigned int last_mask = total_clus & (BITS_PER_LONG - 1);
|
||||
unsigned long *bitmap, clu_bits;
|
||||
|
||||
total_clus &= ~last_mask;
|
||||
for (i = 0; i < total_clus; i += BITS_PER_BYTE) {
|
||||
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
count += used_bit[clu_bits];
|
||||
if (++map_b >= (unsigned int)sb->s_blocksize) {
|
||||
for (i = 0; i < total_clus; i += BITS_PER_LONG) {
|
||||
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
count += hweight_long(*bitmap);
|
||||
map_b += sizeof(long);
|
||||
if (map_b >= (unsigned int)sb->s_blocksize) {
|
||||
map_i++;
|
||||
map_b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_mask) {
|
||||
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
clu_bits &= last_bit_mask[last_mask];
|
||||
count += used_bit[clu_bits];
|
||||
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
clu_bits = lel_to_cpu(*(__le_long *)bitmap);
|
||||
count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask));
|
||||
}
|
||||
|
||||
*ret_count = count;
|
||||
|
@ -288,7 +288,7 @@ get_new:
|
||||
|
||||
mutex_unlock(&EXFAT_SB(sb)->s_lock);
|
||||
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
|
||||
(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
|
||||
(de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG))
|
||||
goto out;
|
||||
ctx->pos = cpos;
|
||||
goto get_new;
|
||||
@ -307,10 +307,17 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
WRAP_DIR_ITER(exfat_iterate) // FIXME!
|
||||
#endif
|
||||
const struct file_operations exfat_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
.iterate_shared = shared_exfat_iterate,
|
||||
#else
|
||||
.iterate = exfat_iterate,
|
||||
#endif
|
||||
.unlocked_ioctl = exfat_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = exfat_compat_ioctl,
|
||||
@ -359,7 +366,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
||||
if (ep->type == EXFAT_VOLUME)
|
||||
return TYPE_VOLUME;
|
||||
if (ep->type == EXFAT_FILE) {
|
||||
if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR)
|
||||
if (le16_to_cpu(ep->dentry.file.attr) & EXFAT_ATTR_SUBDIR)
|
||||
return TYPE_DIR;
|
||||
return TYPE_FILE;
|
||||
}
|
||||
@ -410,19 +417,21 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type)
|
||||
ep->type = EXFAT_VOLUME;
|
||||
} else if (type == TYPE_DIR) {
|
||||
ep->type = EXFAT_FILE;
|
||||
ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR);
|
||||
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_SUBDIR);
|
||||
} else if (type == TYPE_FILE) {
|
||||
ep->type = EXFAT_FILE;
|
||||
ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE);
|
||||
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
}
|
||||
}
|
||||
|
||||
static void exfat_init_stream_entry(struct exfat_dentry *ep,
|
||||
unsigned char flags, unsigned int start_clu,
|
||||
unsigned long long size)
|
||||
unsigned int start_clu, unsigned long long size)
|
||||
{
|
||||
exfat_set_entry_type(ep, TYPE_STREAM);
|
||||
ep->dentry.stream.flags = flags;
|
||||
if (size == 0)
|
||||
ep->dentry.stream.flags = ALLOC_FAT_CHAIN;
|
||||
else
|
||||
ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN;
|
||||
ep->dentry.stream.start_clu = cpu_to_le32(start_clu);
|
||||
ep->dentry.stream.valid_size = cpu_to_le64(size);
|
||||
ep->dentry.stream.size = cpu_to_le64(size);
|
||||
@ -498,9 +507,7 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
exfat_init_stream_entry(ep,
|
||||
(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
|
||||
start_clu, size);
|
||||
exfat_init_stream_entry(ep, start_clu, size);
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
|
||||
|
@ -148,8 +148,7 @@ enum {
|
||||
#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb))
|
||||
#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \
|
||||
((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1))
|
||||
#define BITS_PER_BYTE_MASK 0x7
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1UL << ((clu) - (clu_base))) - 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 */
|
||||
@ -253,6 +252,8 @@ struct exfat_mount_options {
|
||||
discard:1, /* Issue discard requests on deletions */
|
||||
keep_last_dots:1; /* Keep trailing periods in paths */
|
||||
int time_offset; /* Offset of timestamps from UTC (in minutes) */
|
||||
/* Support creating zero-size directory, default: false */
|
||||
bool zero_size_dir;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -382,10 +383,10 @@ static inline int exfat_mode_can_hold_ro(struct inode *inode)
|
||||
static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
|
||||
unsigned short attr, mode_t mode)
|
||||
{
|
||||
if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
|
||||
if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR))
|
||||
mode &= ~0222;
|
||||
|
||||
if (attr & ATTR_SUBDIR)
|
||||
if (attr & EXFAT_ATTR_SUBDIR)
|
||||
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
|
||||
|
||||
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
|
||||
@ -397,18 +398,18 @@ static inline unsigned short exfat_make_attr(struct inode *inode)
|
||||
unsigned short attr = EXFAT_I(inode)->attr;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
attr |= ATTR_SUBDIR;
|
||||
attr |= EXFAT_ATTR_SUBDIR;
|
||||
if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
|
||||
attr |= ATTR_READONLY;
|
||||
attr |= EXFAT_ATTR_READONLY;
|
||||
return attr;
|
||||
}
|
||||
|
||||
static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
|
||||
{
|
||||
if (exfat_mode_can_hold_ro(inode))
|
||||
EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
|
||||
EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY);
|
||||
else
|
||||
EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
|
||||
EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK;
|
||||
}
|
||||
|
||||
static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
|
||||
@ -596,6 +597,9 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
|
||||
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
u8 tz, __le16 time, __le16 date, u8 time_cs);
|
||||
void exfat_truncate_atime(struct timespec64 *ts);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
void exfat_truncate_inode_atime(struct inode *inode);
|
||||
#endif
|
||||
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
|
||||
#else
|
||||
|
@ -64,15 +64,16 @@
|
||||
#define CS_DEFAULT 2
|
||||
|
||||
/* file attributes */
|
||||
#define ATTR_READONLY 0x0001
|
||||
#define ATTR_HIDDEN 0x0002
|
||||
#define ATTR_SYSTEM 0x0004
|
||||
#define ATTR_VOLUME 0x0008
|
||||
#define ATTR_SUBDIR 0x0010
|
||||
#define ATTR_ARCHIVE 0x0020
|
||||
#define EXFAT_ATTR_READONLY 0x0001
|
||||
#define EXFAT_ATTR_HIDDEN 0x0002
|
||||
#define EXFAT_ATTR_SYSTEM 0x0004
|
||||
#define EXFAT_ATTR_VOLUME 0x0008
|
||||
#define EXFAT_ATTR_SUBDIR 0x0010
|
||||
#define EXFAT_ATTR_ARCHIVE 0x0020
|
||||
|
||||
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
|
||||
ATTR_SUBDIR | ATTR_ARCHIVE)
|
||||
#define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \
|
||||
EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \
|
||||
EXFAT_ATTR_ARCHIVE)
|
||||
|
||||
#define BOOTSEC_JUMP_BOOT_LEN 3
|
||||
#define BOOTSEC_FS_NAME_LEN 8
|
||||
|
161
fs/exfat/file.c
161
fs/exfat/file.c
@ -9,8 +9,12 @@
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/cred.h>
|
||||
#endif
|
||||
#include <linux/mount.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
@ -26,7 +30,15 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
|
||||
return err;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_ctime = inode->i_mtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@ -151,7 +163,7 @@ int __exfat_truncate(struct inode *inode)
|
||||
}
|
||||
|
||||
if (ei->type == TYPE_FILE)
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
|
||||
/*
|
||||
* update the directory entry
|
||||
@ -265,7 +277,11 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
|
||||
#else
|
||||
generic_fillattr(&nop_mnt_idmap, inode, stat);
|
||||
#endif
|
||||
#else
|
||||
generic_fillattr(&init_user_ns, inode, stat);
|
||||
#endif
|
||||
@ -355,11 +371,23 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
@ -370,6 +398,7 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
setattr_copy(inode, attr);
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
error = exfat_block_truncate_page(inode, attr->ia_size);
|
||||
@ -390,6 +419,131 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* modified ioctls from fat/file.c by Welmer Almesberger
|
||||
*/
|
||||
static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
|
||||
{
|
||||
u32 attr;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||
inode_lock_shared(inode);
|
||||
#else
|
||||
mutex_lock(&inode->i_mutex);
|
||||
#endif
|
||||
attr = exfat_make_attr(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||
inode_unlock_shared(inode);
|
||||
#else
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
#endif
|
||||
|
||||
return put_user(attr, user_attr);
|
||||
}
|
||||
|
||||
static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
|
||||
int is_dir = S_ISDIR(inode->i_mode);
|
||||
u32 attr, oldattr;
|
||||
struct iattr ia;
|
||||
int err;
|
||||
|
||||
err = get_user(attr, user_attr);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = mnt_want_write_file(file);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
inode_lock(inode);
|
||||
#else
|
||||
mutex_lock(&inode->i_mutex);
|
||||
#endif
|
||||
|
||||
oldattr = exfat_make_attr(inode);
|
||||
|
||||
/*
|
||||
* Mask attributes so we don't set reserved fields.
|
||||
*/
|
||||
attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM |
|
||||
EXFAT_ATTR_ARCHIVE);
|
||||
attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0);
|
||||
|
||||
/* Equivalent to a chmod() */
|
||||
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
||||
ia.ia_ctime = current_time(inode);
|
||||
#else
|
||||
ia.ia_ctime = current_fs_time(inode->i_sb);
|
||||
#endif
|
||||
if (is_dir)
|
||||
ia.ia_mode = exfat_make_mode(sbi, attr, 0777);
|
||||
else
|
||||
ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111));
|
||||
|
||||
/* The root directory has no attributes */
|
||||
if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) {
|
||||
err = -EINVAL;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) &&
|
||||
!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* The security check is questionable... We single
|
||||
* out the RO attribute for checking by the security
|
||||
* module, just because it maps to a file mode.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
err = security_inode_setattr(file_mnt_idmap(file),
|
||||
file->f_path.dentry, &ia);
|
||||
#else
|
||||
err = security_inode_setattr(file_mnt_user_ns(file),
|
||||
file->f_path.dentry, &ia);
|
||||
#endif
|
||||
#else
|
||||
err = security_inode_setattr(file->f_path.dentry, &ia);
|
||||
#endif
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
/* This MUST be done before doing anything irreversible... */
|
||||
err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia);
|
||||
#else
|
||||
err = exfat_setattr(file_mnt_user_ns(file), file->f_path.dentry, &ia);
|
||||
#endif
|
||||
#else
|
||||
err = exfat_setattr(file->f_path.dentry, &ia);
|
||||
#endif
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
fsnotify_change(file->f_path.dentry, ia.ia_valid);
|
||||
|
||||
exfat_save_attr(inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
out_unlock_inode:
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
inode_unlock(inode);
|
||||
#else
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
#endif
|
||||
mnt_drop_write_file(file);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
|
||||
@ -432,8 +586,13 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
u32 __user *user_attr = (u32 __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case FAT_IOCTL_GET_ATTRIBUTES:
|
||||
return exfat_ioctl_get_attributes(inode, user_attr);
|
||||
case FAT_IOCTL_SET_ATTRIBUTES:
|
||||
return exfat_ioctl_set_attributes(filp, user_attr);
|
||||
case FITRIM:
|
||||
return exfat_ioctl_fitrim(inode, arg);
|
||||
default:
|
||||
|
@ -28,6 +28,9 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
bool is_dir = (ei->type == TYPE_DIR) ? true : false;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct timespec64 ts;
|
||||
#endif
|
||||
|
||||
if (inode->i_ino == EXFAT_ROOT_INO)
|
||||
return 0;
|
||||
@ -57,6 +60,20 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
&ep->dentry.file.create_time,
|
||||
&ep->dentry.file.create_date,
|
||||
&ep->dentry.file.create_time_cs);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
ts = inode_get_mtime(inode);
|
||||
exfat_set_entry_time(sbi, &ts,
|
||||
&ep->dentry.file.modify_tz,
|
||||
&ep->dentry.file.modify_time,
|
||||
&ep->dentry.file.modify_date,
|
||||
&ep->dentry.file.modify_time_cs);
|
||||
ts = inode_get_atime(inode);
|
||||
exfat_set_entry_time(sbi, &ts,
|
||||
&ep->dentry.file.access_tz,
|
||||
&ep->dentry.file.access_time,
|
||||
&ep->dentry.file.access_date,
|
||||
NULL);
|
||||
#else
|
||||
exfat_set_entry_time(sbi, &inode->i_mtime,
|
||||
&ep->dentry.file.modify_tz,
|
||||
&ep->dentry.file.modify_time,
|
||||
@ -67,6 +84,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
&ep->dentry.file.access_time,
|
||||
&ep->dentry.file.access_date,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
/* File size should be zero if there is no cluster allocated */
|
||||
on_disk_size = i_size_read(inode);
|
||||
@ -380,7 +398,15 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
|
||||
if (to > i_size_read(inode)) {
|
||||
truncate_pagecache(inode, i_size_read(inode));
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@ -436,13 +462,21 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
|
||||
if (err < len)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
|
||||
if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) {
|
||||
if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
@ -629,7 +663,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
inode->i_generation = prandom_u32();
|
||||
#endif
|
||||
|
||||
if (info->attr & ATTR_SUBDIR) { /* directory */
|
||||
if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */
|
||||
inode->i_generation &= ~1;
|
||||
inode->i_mode = exfat_make_mode(sbi, info->attr, 0777);
|
||||
inode->i_op = &exfat_dir_inode_operations;
|
||||
@ -658,10 +692,22 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
exfat_save_attr(inode, info->attr);
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, info->mtime);
|
||||
#else
|
||||
inode->i_mtime = info->mtime;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode_set_ctime_to_ts(inode, info->mtime);
|
||||
#else
|
||||
inode->i_ctime = info->mtime;
|
||||
#endif
|
||||
ei->i_crtime = info->crtime;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_atime_to_ts(inode, info->atime);
|
||||
#else
|
||||
inode->i_atime = info->atime;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -163,6 +163,16 @@ void exfat_truncate_atime(struct timespec *ts)
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
void exfat_truncate_inode_atime(struct inode *inode)
|
||||
{
|
||||
struct timespec64 atime = inode_get_atime(inode);
|
||||
|
||||
exfat_truncate_atime(&atime);
|
||||
inode_set_atime_to_ts(inode, atime);
|
||||
}
|
||||
#endif
|
||||
|
||||
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
|
||||
{
|
||||
int i;
|
||||
|
156
fs/exfat/namei.c
156
fs/exfat/namei.c
@ -378,14 +378,20 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (exfat_check_max_dentries(inode))
|
||||
return -ENOSPC;
|
||||
|
||||
/*
|
||||
* Allocate new cluster to this directory
|
||||
*/
|
||||
if (ei->start_clu != EXFAT_EOF_CLUSTER) {
|
||||
/* we trust p_dir->size regardless of FAT type */
|
||||
if (exfat_find_last_cluster(sb, p_dir, &last_clu))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Allocate new cluster to this directory
|
||||
*/
|
||||
exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags);
|
||||
} else {
|
||||
/* This directory is empty */
|
||||
exfat_chain_set(&clu, EXFAT_EOF_CLUSTER, 0,
|
||||
ALLOC_NO_FAT_CHAIN);
|
||||
}
|
||||
|
||||
/* allocate a cluster */
|
||||
ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode));
|
||||
@ -395,6 +401,11 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (exfat_zeroed_cluster(inode, clu.dir))
|
||||
return -EIO;
|
||||
|
||||
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
|
||||
ei->start_clu = clu.dir;
|
||||
p_dir->dir = clu.dir;
|
||||
}
|
||||
|
||||
/* append to the FAT chain */
|
||||
if (clu.flags != p_dir->flags) {
|
||||
/* no-fat-chain bit is disabled,
|
||||
@ -534,7 +545,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type == TYPE_DIR) {
|
||||
if (type == TYPE_DIR && !sbi->options.zero_size_dir) {
|
||||
ret = exfat_alloc_new_dir(inode, &clu);
|
||||
if (ret)
|
||||
goto out;
|
||||
@ -561,12 +572,15 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
info->type = type;
|
||||
|
||||
if (type == TYPE_FILE) {
|
||||
info->attr = ATTR_ARCHIVE;
|
||||
info->attr = EXFAT_ATTR_ARCHIVE;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
info->size = 0;
|
||||
info->num_subdirs = 0;
|
||||
} else {
|
||||
info->attr = ATTR_SUBDIR;
|
||||
info->attr = EXFAT_ATTR_SUBDIR;
|
||||
if (sbi->options.zero_size_dir)
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
else
|
||||
info->start_clu = start_clu;
|
||||
info->size = clu_size;
|
||||
info->num_subdirs = EXFAT_MIN_SUBDIR;
|
||||
@ -611,7 +625,15 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
#else
|
||||
dir->i_mtime = inode_set_ctime_current(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@ -632,14 +654,23 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
inode->i_version++;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
@ -709,7 +740,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
if ((info->type == TYPE_FILE) && (info->size == 0)) {
|
||||
if (info->size == 0) {
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
} else {
|
||||
@ -888,24 +919,43 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||
#else
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(dir);
|
||||
exfat_truncate_inode_atime(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
|
||||
clear_nlink(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
exfat_unhash_inode(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
@ -950,7 +1000,15 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
#else
|
||||
dir->i_mtime = inode_set_ctime_current(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@ -971,14 +1029,23 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#else
|
||||
inode->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
@ -1000,6 +1067,9 @@ static int exfat_check_dir_empty(struct super_block *sb,
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
|
||||
if (p_dir->dir == EXFAT_EOF_CLUSTER)
|
||||
return 0;
|
||||
|
||||
exfat_chain_dup(&clu, p_dir);
|
||||
|
||||
while (clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
@ -1093,12 +1163,21 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
#else
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(dir);
|
||||
exfat_truncate_inode_atime(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
@ -1106,12 +1185,21 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
drop_nlink(dir);
|
||||
|
||||
clear_nlink(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
exfat_unhash_inode(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
@ -1160,8 +1248,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
|
||||
*epnew = *epold;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(new_bh, sync);
|
||||
brelse(old_bh);
|
||||
@ -1192,8 +1280,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
ei->entry = newentry;
|
||||
} else {
|
||||
if (exfat_get_entry_type(epold) == TYPE_FILE) {
|
||||
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epold->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(old_bh, sync);
|
||||
brelse(old_bh);
|
||||
@ -1241,8 +1329,8 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
||||
|
||||
*epnew = *epmov;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
|
||||
brelse(mov_bh);
|
||||
@ -1383,7 +1471,8 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||
}
|
||||
|
||||
/* Free the clusters if new_inode is a dir(as if exfat_rmdir) */
|
||||
if (new_entry_type == TYPE_DIR) {
|
||||
if (new_entry_type == TYPE_DIR &&
|
||||
new_ei->start_clu != EXFAT_EOF_CLUSTER) {
|
||||
/* new_ei, new_clu_to_free */
|
||||
struct exfat_chain new_clu_to_free;
|
||||
|
||||
@ -1463,13 +1552,22 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
new_dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
|
||||
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
|
||||
#else
|
||||
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
|
||||
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
|
||||
#endif
|
||||
#else
|
||||
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
|
||||
EXFAT_I(new_dir)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
exfat_truncate_inode_atime(new_dir);
|
||||
#else
|
||||
exfat_truncate_atime(&new_dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(new_dir))
|
||||
exfat_sync_inode(new_dir);
|
||||
else
|
||||
@ -1496,7 +1594,9 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
old_dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 6, 0)
|
||||
old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
|
||||
#endif
|
||||
#else
|
||||
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@ -1518,8 +1618,12 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
WARN_ON(new_inode->i_nlink == 0);
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
EXFAT_I(new_inode)->i_crtime = current_time(new_inode);
|
||||
#else
|
||||
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
|
||||
current_time(new_inode);
|
||||
#endif
|
||||
#else
|
||||
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
|
||||
CURRENT_TIME_SEC;
|
||||
|
@ -195,6 +195,8 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||
seq_puts(m, ",sys_tz");
|
||||
else if (opts->time_offset)
|
||||
seq_printf(m, ",time_offset=%d", opts->time_offset);
|
||||
if (opts->zero_size_dir)
|
||||
seq_puts(m, ",zero_size_dir");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -278,6 +280,7 @@ enum {
|
||||
Opt_keep_last_dots,
|
||||
Opt_sys_tz,
|
||||
Opt_time_offset,
|
||||
Opt_zero_size_dir,
|
||||
|
||||
/* Deprecated options */
|
||||
Opt_utf8,
|
||||
@ -323,6 +326,7 @@ static const struct fs_parameter_spec exfat_param_specs[] = {
|
||||
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
|
||||
fsparam_flag("sys_tz", Opt_sys_tz),
|
||||
fsparam_s32("time_offset", Opt_time_offset),
|
||||
fsparam_flag("zero_size_dir", Opt_zero_size_dir),
|
||||
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
|
||||
NULL),
|
||||
__fsparam(NULL, "debug", Opt_debug, fs_param_deprecated,
|
||||
@ -403,6 +407,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
return -EINVAL;
|
||||
opts->time_offset = result.int_32;
|
||||
break;
|
||||
case Opt_zero_size_dir:
|
||||
opts->zero_size_dir = true;
|
||||
break;
|
||||
case Opt_utf8:
|
||||
case Opt_debug:
|
||||
case Opt_namecase:
|
||||
@ -435,6 +442,7 @@ enum {
|
||||
Opt_debug,
|
||||
Opt_namecase,
|
||||
Opt_codepage,
|
||||
Opt_zero_size_dir,
|
||||
Opt_fs,
|
||||
};
|
||||
|
||||
@ -454,6 +462,7 @@ static const match_table_t exfat_tokens = {
|
||||
{Opt_namecase, "namecase=%u"},
|
||||
{Opt_debug, "debug"},
|
||||
{Opt_utf8, "utf8"},
|
||||
{Opt_zero_size_dir, "zero_size_dir"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
@ -545,6 +554,9 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
case Opt_namecase:
|
||||
case Opt_codepage:
|
||||
break;
|
||||
case Opt_zero_size_dir:
|
||||
opts->zero_size_dir = true;
|
||||
break;
|
||||
default:
|
||||
if (!silent) {
|
||||
exfat_err(sb,
|
||||
@ -628,7 +640,7 @@ static int exfat_read_root(struct inode *inode)
|
||||
inode->i_version++;
|
||||
#endif
|
||||
inode->i_generation = 0;
|
||||
inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
|
||||
inode->i_mode = exfat_make_mode(sbi, EXFAT_ATTR_SUBDIR, 0777);
|
||||
inode->i_op = &exfat_dir_inode_operations;
|
||||
inode->i_fop = &exfat_dir_operations;
|
||||
|
||||
@ -637,15 +649,24 @@ static int exfat_read_root(struct inode *inode)
|
||||
ei->i_size_aligned = i_size_read(inode);
|
||||
ei->i_size_ondisk = i_size_read(inode);
|
||||
|
||||
exfat_save_attr(inode, ATTR_SUBDIR);
|
||||
exfat_save_attr(inode, EXFAT_ATTR_SUBDIR);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
ei->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = ei->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
|
||||
current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
|
||||
CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user