diff --git a/exfat_super.c b/exfat_super.c index 20e60d7..1038580 100644 --- a/exfat_super.c +++ b/exfat_super.c @@ -261,6 +261,150 @@ static void __set_sb_clean(struct super_block *sb) #endif } +static int __exfat_revalidate(struct dentry *dentry) +{ + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; +#endif + + if (dentry->d_inode) + return 1; + return __exfat_revalidate(dentry); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +#else +static int exfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + if (flags & LOOKUP_RCU) + return -ECHILD; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + unsigned int flags; + + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + flags = nd ? nd->flags : 0; +#else + flags = nd ? nd->flags : 0; +#endif + + if (dentry->d_inode) + return 1; + + if (!flags) + return 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,00) + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; +#else + if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) { + if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + } +#endif + + return __exfat_revalidate(dentry); +} + +static unsigned int __exfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int exfat_striptail_len(const struct qstr *qstr) +{ + return __exfat_striptail_len(qstr->len, qstr->name); +} + +static int exfat_d_hash(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +{ + qstr->hash = full_name_hash(qstr->name, exfat_striptail_len(qstr)); + return 0; +} + +static int exfat_d_hashi(const struct dentry *dentry, const struct inode *inode, + struct qstr *qstr) +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = exfat_striptail_len(qstr); + + hash = init_name_hash(); + while (len--) + hash = partial_name_hash(nls_tolower(t, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +static int exfat_cmpi(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + struct nls_table *t = EXFAT_SB(parent->d_sb)->nls_io; + unsigned int alen, blen; + + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static int exfat_cmp(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + unsigned int alen, blen; + + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static const struct dentry_operations exfat_ci_dentry_ops = { + .d_revalidate = exfat_revalidate_ci, + .d_hash = exfat_d_hashi, + .d_compare = exfat_cmpi, +}; + +static const struct dentry_operations exfat_dentry_ops = { + .d_revalidate = exfat_revalidate, + .d_hash = exfat_d_hash, + .d_compare = exfat_cmp, +}; + /*======================================================================*/ /* Directory Entry Operations */ /*======================================================================*/ @@ -1409,9 +1553,13 @@ static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter loff_t offset) #else static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, +#ifdef CONFIG_AIO_OPTIMIZATION + struct iov_iter *iter, loff_t offset) +#else const struct iovec *iov, loff_t offset, unsigned long nr_segs) #endif +#endif { struct inode *inode = iocb->ki_filp->f_mapping->host; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) @@ -1422,8 +1570,13 @@ static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, if (rw == WRITE) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) if (EXFAT_I(inode)->mmu_private < (offset + iov_iter_count(iter))) +#else +#ifdef CONFIG_AIO_OPTIMIZATION + if (EXFAT_I(inode)->mmu_private < + (offset + iov_iter_count(iter))) #else if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) +#endif #endif return 0; } @@ -1431,10 +1584,15 @@ static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, exfat_get_block); +#else +#ifdef CONFIG_AIO_OPTIMIZATION + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, + exfat_get_block); #else ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, exfat_get_block); #endif +#endif #else ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, exfat_get_block, NULL); @@ -1444,9 +1602,13 @@ static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, if ((ret < 0) && (rw & WRITE)) #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) exfat_write_failed(mapping, offset+iov_iter_count(iter)); +#else +#ifdef CONFIG_AIO_OPTIMIZATION + exfat_write_failed(mapping, offset+iov_iter_count(iter)); #else exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); #endif +#endif #endif return ret; } @@ -2080,6 +2242,14 @@ static int exfat_read_root(struct inode *inode) return 0; } +static void setup_dops(struct super_block *sb) +{ + if (EXFAT_SB(sb)->options.casesensitive == 0) + sb->s_d_op = &exfat_ci_dentry_ops; + else + sb->s_d_op = &exfat_dentry_ops; +} + static int exfat_fill_super(struct super_block *sb, void *data, int silent) { struct inode *root_inode = NULL; @@ -2109,6 +2279,8 @@ static int exfat_fill_super(struct super_block *sb, void *data, int silent) if (error) goto out_fail; + setup_dops(sb); + error = -EIO; sb_min_blocksize(sb, 512); sb->s_maxbytes = 0x7fffffffffffffffLL; /* maximum file size */ diff --git a/exfat_version.h b/exfat_version.h index ac24433..414d21a 100644 --- a/exfat_version.h +++ b/exfat_version.h @@ -16,4 +16,4 @@ /* */ /************************************************************************/ -#define EXFAT_VERSION "1.2.7" +#define EXFAT_VERSION "1.2.8"