Skip to content

Commit

Permalink
exfat: add ioctls for accessing attributes
Browse files Browse the repository at this point in the history
Add GET and SET attributes ioctls to enable attribute modification.
We already do this in FAT and a few userspace utils made for it would
benefit from this also working on exFAT, namely fatattr.

Signed-off-by: Jan Cincera <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
  • Loading branch information
hcincera authored and namjaejeon committed Aug 30, 2023
1 parent 967fa6b commit 6271729
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
6 changes: 6 additions & 0 deletions exfat_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ enum {
#define DIR_CACHE_SIZE \
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)

/*
* attribute ioctls, same as their FAT equivalents.
*/
#define EXFAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32)
#define EXFAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, __u32)

struct exfat_dentry_namebuf {
char *lfn;
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
Expand Down
93 changes: 93 additions & 0 deletions file.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#endif
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/fsnotify.h>
#include <linux/security.h>

#include "exfat_raw.h"
#include "exfat_fs.h"
Expand Down Expand Up @@ -390,6 +392,92 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
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 &= (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE);
attr |= (is_dir ? 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 != ATTR_SUBDIR) {
err = -EINVAL;
goto out_unlock_inode;
}

if (((attr | oldattr) & 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)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
Expand Down Expand Up @@ -432,8 +520,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 EXFAT_IOCTL_GET_ATTRIBUTES:
return exfat_ioctl_get_attributes(inode, user_attr);
case EXFAT_IOCTL_SET_ATTRIBUTES:
return exfat_ioctl_set_attributes(filp, user_attr);
case FITRIM:
return exfat_ioctl_fitrim(inode, arg);
default:
Expand Down

0 comments on commit 6271729

Please sign in to comment.