Commit dc737f11 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull exfat updates from Namjae Jeon:

 - Add ioctls to get and set file attribute that is used in
   the fatattr util

 - Add zero_size_dir mount option to avoid allocating a cluster
   when creating a directory

* tag 'exfat-for-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat:
  exfat: support create zero-size directory
  exfat: support handle zero-size directory
  exfat: add ioctls for accessing attributes
parents 87a201b4 ee785c15
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -287,7 +287,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)

	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;
@@ -359,7 +359,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 +410,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);
@@ -488,9 +490,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);

+8 −6
Original line number Diff line number Diff line
@@ -234,6 +234,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;
};

/*
@@ -357,10 +359,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;
@@ -372,18 +374,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,
+10 −9
Original line number Diff line number Diff line
@@ -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 ATTR_RWMASK		(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
				 ATTR_SUBDIR | ATTR_ARCHIVE)
#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 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
+96 −1
Original line number Diff line number Diff line
@@ -8,6 +8,9 @@
#include <linux/cred.h>
#include <linux/buffer_head.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"
@@ -144,7 +147,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
@@ -315,6 +318,93 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
	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;

	inode_lock_shared(inode);
	attr = exfat_make_attr(inode);
	inode_unlock_shared(inode);

	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;
	inode_lock(inode);

	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;
	ia.ia_ctime = current_time(inode);
	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.
	 */
	err = security_inode_setattr(file_mnt_idmap(file),
				     file->f_path.dentry, &ia);
	if (err)
		goto out_unlock_inode;

	/* This MUST be done before doing anything irreversible... */
	err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia);
	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:
	inode_unlock(inode);
	mnt_drop_write_file(file);
out:
	return err;
}

static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
{
	struct fstrim_range range;
@@ -345,8 +435,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:
+3 −3
Original line number Diff line number Diff line
@@ -400,9 +400,9 @@ 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)) {
		inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
		ei->attr |= ATTR_ARCHIVE;
		ei->attr |= EXFAT_ATTR_ARCHIVE;
		mark_inode_dirty(inode);
	}

@@ -550,7 +550,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
	inode_inc_iversion(inode);
	inode->i_generation = get_random_u32();

	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;
Loading