Skip to content

Commit dc737f1

Browse files
committed
Merge tag 'exfat-for-6.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat
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
2 parents 87a201b + ee785c1 commit dc737f1

File tree

7 files changed

+171
-48
lines changed

7 files changed

+171
-48
lines changed

fs/exfat/dir.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
287287

288288
mutex_unlock(&EXFAT_SB(sb)->s_lock);
289289
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
290-
(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
290+
(de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG))
291291
goto out;
292292
ctx->pos = cpos;
293293
goto get_new;
@@ -359,7 +359,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
359359
if (ep->type == EXFAT_VOLUME)
360360
return TYPE_VOLUME;
361361
if (ep->type == EXFAT_FILE) {
362-
if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR)
362+
if (le16_to_cpu(ep->dentry.file.attr) & EXFAT_ATTR_SUBDIR)
363363
return TYPE_DIR;
364364
return TYPE_FILE;
365365
}
@@ -410,19 +410,21 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type)
410410
ep->type = EXFAT_VOLUME;
411411
} else if (type == TYPE_DIR) {
412412
ep->type = EXFAT_FILE;
413-
ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR);
413+
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_SUBDIR);
414414
} else if (type == TYPE_FILE) {
415415
ep->type = EXFAT_FILE;
416-
ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE);
416+
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_ARCHIVE);
417417
}
418418
}
419419

420420
static void exfat_init_stream_entry(struct exfat_dentry *ep,
421-
unsigned char flags, unsigned int start_clu,
422-
unsigned long long size)
421+
unsigned int start_clu, unsigned long long size)
423422
{
424423
exfat_set_entry_type(ep, TYPE_STREAM);
425-
ep->dentry.stream.flags = flags;
424+
if (size == 0)
425+
ep->dentry.stream.flags = ALLOC_FAT_CHAIN;
426+
else
427+
ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN;
426428
ep->dentry.stream.start_clu = cpu_to_le32(start_clu);
427429
ep->dentry.stream.valid_size = cpu_to_le64(size);
428430
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,
488490
if (!ep)
489491
return -EIO;
490492

491-
exfat_init_stream_entry(ep,
492-
(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
493-
start_clu, size);
493+
exfat_init_stream_entry(ep, start_clu, size);
494494
exfat_update_bh(bh, IS_DIRSYNC(inode));
495495
brelse(bh);
496496

fs/exfat/exfat_fs.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ struct exfat_mount_options {
234234
discard:1, /* Issue discard requests on deletions */
235235
keep_last_dots:1; /* Keep trailing periods in paths */
236236
int time_offset; /* Offset of timestamps from UTC (in minutes) */
237+
/* Support creating zero-size directory, default: false */
238+
bool zero_size_dir;
237239
};
238240

239241
/*
@@ -357,10 +359,10 @@ static inline int exfat_mode_can_hold_ro(struct inode *inode)
357359
static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
358360
unsigned short attr, mode_t mode)
359361
{
360-
if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
362+
if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR))
361363
mode &= ~0222;
362364

363-
if (attr & ATTR_SUBDIR)
365+
if (attr & EXFAT_ATTR_SUBDIR)
364366
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
365367

366368
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
@@ -372,18 +374,18 @@ static inline unsigned short exfat_make_attr(struct inode *inode)
372374
unsigned short attr = EXFAT_I(inode)->attr;
373375

374376
if (S_ISDIR(inode->i_mode))
375-
attr |= ATTR_SUBDIR;
377+
attr |= EXFAT_ATTR_SUBDIR;
376378
if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
377-
attr |= ATTR_READONLY;
379+
attr |= EXFAT_ATTR_READONLY;
378380
return attr;
379381
}
380382

381383
static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
382384
{
383385
if (exfat_mode_can_hold_ro(inode))
384-
EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
386+
EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY);
385387
else
386-
EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
388+
EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK;
387389
}
388390

389391
static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,

fs/exfat/exfat_raw.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,16 @@
6464
#define CS_DEFAULT 2
6565

6666
/* file attributes */
67-
#define ATTR_READONLY 0x0001
68-
#define ATTR_HIDDEN 0x0002
69-
#define ATTR_SYSTEM 0x0004
70-
#define ATTR_VOLUME 0x0008
71-
#define ATTR_SUBDIR 0x0010
72-
#define ATTR_ARCHIVE 0x0020
73-
74-
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
75-
ATTR_SUBDIR | ATTR_ARCHIVE)
67+
#define EXFAT_ATTR_READONLY 0x0001
68+
#define EXFAT_ATTR_HIDDEN 0x0002
69+
#define EXFAT_ATTR_SYSTEM 0x0004
70+
#define EXFAT_ATTR_VOLUME 0x0008
71+
#define EXFAT_ATTR_SUBDIR 0x0010
72+
#define EXFAT_ATTR_ARCHIVE 0x0020
73+
74+
#define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \
75+
EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \
76+
EXFAT_ATTR_ARCHIVE)
7677

7778
#define BOOTSEC_JUMP_BOOT_LEN 3
7879
#define BOOTSEC_FS_NAME_LEN 8

fs/exfat/file.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
#include <linux/cred.h>
99
#include <linux/buffer_head.h>
1010
#include <linux/blkdev.h>
11+
#include <linux/fsnotify.h>
12+
#include <linux/security.h>
13+
#include <linux/msdos_fs.h>
1114

1215
#include "exfat_raw.h"
1316
#include "exfat_fs.h"
@@ -144,7 +147,7 @@ int __exfat_truncate(struct inode *inode)
144147
}
145148

146149
if (ei->type == TYPE_FILE)
147-
ei->attr |= ATTR_ARCHIVE;
150+
ei->attr |= EXFAT_ATTR_ARCHIVE;
148151

149152
/*
150153
* update the directory entry
@@ -315,6 +318,93 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
315318
return error;
316319
}
317320

321+
/*
322+
* modified ioctls from fat/file.c by Welmer Almesberger
323+
*/
324+
static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
325+
{
326+
u32 attr;
327+
328+
inode_lock_shared(inode);
329+
attr = exfat_make_attr(inode);
330+
inode_unlock_shared(inode);
331+
332+
return put_user(attr, user_attr);
333+
}
334+
335+
static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
336+
{
337+
struct inode *inode = file_inode(file);
338+
struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
339+
int is_dir = S_ISDIR(inode->i_mode);
340+
u32 attr, oldattr;
341+
struct iattr ia;
342+
int err;
343+
344+
err = get_user(attr, user_attr);
345+
if (err)
346+
goto out;
347+
348+
err = mnt_want_write_file(file);
349+
if (err)
350+
goto out;
351+
inode_lock(inode);
352+
353+
oldattr = exfat_make_attr(inode);
354+
355+
/*
356+
* Mask attributes so we don't set reserved fields.
357+
*/
358+
attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM |
359+
EXFAT_ATTR_ARCHIVE);
360+
attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0);
361+
362+
/* Equivalent to a chmod() */
363+
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
364+
ia.ia_ctime = current_time(inode);
365+
if (is_dir)
366+
ia.ia_mode = exfat_make_mode(sbi, attr, 0777);
367+
else
368+
ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111));
369+
370+
/* The root directory has no attributes */
371+
if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) {
372+
err = -EINVAL;
373+
goto out_unlock_inode;
374+
}
375+
376+
if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) &&
377+
!capable(CAP_LINUX_IMMUTABLE)) {
378+
err = -EPERM;
379+
goto out_unlock_inode;
380+
}
381+
382+
/*
383+
* The security check is questionable... We single
384+
* out the RO attribute for checking by the security
385+
* module, just because it maps to a file mode.
386+
*/
387+
err = security_inode_setattr(file_mnt_idmap(file),
388+
file->f_path.dentry, &ia);
389+
if (err)
390+
goto out_unlock_inode;
391+
392+
/* This MUST be done before doing anything irreversible... */
393+
err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia);
394+
if (err)
395+
goto out_unlock_inode;
396+
397+
fsnotify_change(file->f_path.dentry, ia.ia_valid);
398+
399+
exfat_save_attr(inode, attr);
400+
mark_inode_dirty(inode);
401+
out_unlock_inode:
402+
inode_unlock(inode);
403+
mnt_drop_write_file(file);
404+
out:
405+
return err;
406+
}
407+
318408
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
319409
{
320410
struct fstrim_range range;
@@ -345,8 +435,13 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
345435
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
346436
{
347437
struct inode *inode = file_inode(filp);
438+
u32 __user *user_attr = (u32 __user *)arg;
348439

349440
switch (cmd) {
441+
case FAT_IOCTL_GET_ATTRIBUTES:
442+
return exfat_ioctl_get_attributes(inode, user_attr);
443+
case FAT_IOCTL_SET_ATTRIBUTES:
444+
return exfat_ioctl_set_attributes(filp, user_attr);
350445
case FITRIM:
351446
return exfat_ioctl_fitrim(inode, arg);
352447
default:

fs/exfat/inode.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,9 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
400400
if (err < len)
401401
exfat_write_failed(mapping, pos+len);
402402

403-
if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) {
403+
if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
404404
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
405-
ei->attr |= ATTR_ARCHIVE;
405+
ei->attr |= EXFAT_ATTR_ARCHIVE;
406406
mark_inode_dirty(inode);
407407
}
408408

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

553-
if (info->attr & ATTR_SUBDIR) { /* directory */
553+
if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */
554554
inode->i_generation &= ~1;
555555
inode->i_mode = exfat_make_mode(sbi, info->attr, 0777);
556556
inode->i_op = &exfat_dir_inode_operations;

0 commit comments

Comments
 (0)