Skip to content

Commit d01579d

Browse files
ethan-ferguson-zetiernamjaejeon
authored andcommitted
exfat: Add support for FS_IOC_{GET,SET}FSLABEL
Add support for reading / writing to the exfat volume label from the FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls Co-developed-by: Yuezhang Mo <[email protected]> Signed-off-by: Yuezhang Mo <[email protected]> Signed-off-by: Ethan Ferguson <[email protected]> Reviewed-by: Sungjong Seo <[email protected]> Signed-off-by: Namjae Jeon <[email protected]>
1 parent 29c0636 commit d01579d

File tree

5 files changed

+226
-1
lines changed

5 files changed

+226
-1
lines changed

fs/exfat/dir.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,3 +1244,163 @@ int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir)
12441244

12451245
return count;
12461246
}
1247+
1248+
static int exfat_get_volume_label_dentry(struct super_block *sb,
1249+
struct exfat_entry_set_cache *es)
1250+
{
1251+
int i;
1252+
int dentry = 0;
1253+
unsigned int type;
1254+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
1255+
struct exfat_hint_femp hint_femp;
1256+
struct exfat_inode_info *ei = EXFAT_I(sb->s_root->d_inode);
1257+
struct exfat_chain clu;
1258+
struct exfat_dentry *ep;
1259+
struct buffer_head *bh;
1260+
1261+
hint_femp.eidx = EXFAT_HINT_NONE;
1262+
exfat_chain_set(&clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN);
1263+
1264+
while (clu.dir != EXFAT_EOF_CLUSTER) {
1265+
for (i = 0; i < sbi->dentries_per_clu; i++, dentry++) {
1266+
ep = exfat_get_dentry(sb, &clu, i, &bh);
1267+
if (!ep)
1268+
return -EIO;
1269+
1270+
type = exfat_get_entry_type(ep);
1271+
if (hint_femp.eidx == EXFAT_HINT_NONE) {
1272+
if (type == TYPE_DELETED || type == TYPE_UNUSED) {
1273+
hint_femp.cur = clu;
1274+
hint_femp.eidx = dentry;
1275+
hint_femp.count = 1;
1276+
}
1277+
}
1278+
1279+
if (type == TYPE_UNUSED) {
1280+
brelse(bh);
1281+
goto not_found;
1282+
}
1283+
1284+
if (type != TYPE_VOLUME) {
1285+
brelse(bh);
1286+
continue;
1287+
}
1288+
1289+
memset(es, 0, sizeof(*es));
1290+
es->sb = sb;
1291+
es->bh = es->__bh;
1292+
es->bh[0] = bh;
1293+
es->num_bh = 1;
1294+
es->start_off = EXFAT_DEN_TO_B(i) % sb->s_blocksize;
1295+
1296+
return 0;
1297+
}
1298+
1299+
if (exfat_get_next_cluster(sb, &(clu.dir)))
1300+
return -EIO;
1301+
}
1302+
1303+
not_found:
1304+
if (hint_femp.eidx == EXFAT_HINT_NONE) {
1305+
hint_femp.cur.dir = EXFAT_EOF_CLUSTER;
1306+
hint_femp.eidx = dentry;
1307+
hint_femp.count = 0;
1308+
}
1309+
1310+
ei->hint_femp = hint_femp;
1311+
1312+
return -ENOENT;
1313+
}
1314+
1315+
int exfat_read_volume_label(struct super_block *sb, struct exfat_uni_name *label_out)
1316+
{
1317+
int ret, i;
1318+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
1319+
struct exfat_entry_set_cache es;
1320+
struct exfat_dentry *ep;
1321+
1322+
mutex_lock(&sbi->s_lock);
1323+
1324+
memset(label_out, 0, sizeof(*label_out));
1325+
ret = exfat_get_volume_label_dentry(sb, &es);
1326+
if (ret < 0) {
1327+
/*
1328+
* ENOENT signifies that a volume label dentry doesn't exist
1329+
* We will treat this as an empty volume label and not fail.
1330+
*/
1331+
if (ret == -ENOENT)
1332+
ret = 0;
1333+
1334+
goto unlock;
1335+
}
1336+
1337+
ep = exfat_get_dentry_cached(&es, 0);
1338+
label_out->name_len = ep->dentry.volume_label.char_count;
1339+
if (label_out->name_len > EXFAT_VOLUME_LABEL_LEN) {
1340+
ret = -EIO;
1341+
exfat_put_dentry_set(&es, false);
1342+
goto unlock;
1343+
}
1344+
1345+
for (i = 0; i < label_out->name_len; i++)
1346+
label_out->name[i] = le16_to_cpu(ep->dentry.volume_label.volume_label[i]);
1347+
1348+
exfat_put_dentry_set(&es, false);
1349+
unlock:
1350+
mutex_unlock(&sbi->s_lock);
1351+
return ret;
1352+
}
1353+
1354+
int exfat_write_volume_label(struct super_block *sb,
1355+
struct exfat_uni_name *label)
1356+
{
1357+
int ret, i;
1358+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
1359+
struct inode *root_inode = sb->s_root->d_inode;
1360+
struct exfat_entry_set_cache es;
1361+
struct exfat_chain clu;
1362+
struct exfat_dentry *ep;
1363+
1364+
if (label->name_len > EXFAT_VOLUME_LABEL_LEN)
1365+
return -EINVAL;
1366+
1367+
mutex_lock(&sbi->s_lock);
1368+
1369+
ret = exfat_get_volume_label_dentry(sb, &es);
1370+
if (ret == -ENOENT) {
1371+
if (label->name_len == 0) {
1372+
/* No volume label dentry, no need to clear */
1373+
ret = 0;
1374+
goto unlock;
1375+
}
1376+
1377+
ret = exfat_find_empty_entry(root_inode, &clu, 1, &es);
1378+
}
1379+
1380+
if (ret < 0)
1381+
goto unlock;
1382+
1383+
ep = exfat_get_dentry_cached(&es, 0);
1384+
1385+
if (label->name_len == 0 && ep->dentry.volume_label.char_count == 0) {
1386+
/* volume label had been cleared */
1387+
exfat_put_dentry_set(&es, 0);
1388+
goto unlock;
1389+
}
1390+
1391+
memset(ep, 0, sizeof(*ep));
1392+
ep->type = EXFAT_VOLUME;
1393+
1394+
for (i = 0; i < label->name_len; i++)
1395+
ep->dentry.volume_label.volume_label[i] =
1396+
cpu_to_le16(label->name[i]);
1397+
1398+
ep->dentry.volume_label.char_count = label->name_len;
1399+
es.modified = true;
1400+
1401+
ret = exfat_put_dentry_set(&es, IS_DIRSYNC(root_inode));
1402+
1403+
unlock:
1404+
mutex_unlock(&sbi->s_lock);
1405+
return ret;
1406+
}

fs/exfat/exfat_fs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,9 @@ int exfat_force_shutdown(struct super_block *sb, u32 flags);
477477
/* namei.c */
478478
extern const struct dentry_operations exfat_dentry_ops;
479479
extern const struct dentry_operations exfat_utf8_dentry_ops;
480+
int exfat_find_empty_entry(struct inode *inode,
481+
struct exfat_chain *p_dir, int num_entries,
482+
struct exfat_entry_set_cache *es);
480483

481484
/* cache.c */
482485
int exfat_cache_init(void);
@@ -517,6 +520,10 @@ int exfat_get_empty_dentry_set(struct exfat_entry_set_cache *es,
517520
unsigned int num_entries);
518521
int exfat_put_dentry_set(struct exfat_entry_set_cache *es, int sync);
519522
int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
523+
int exfat_read_volume_label(struct super_block *sb,
524+
struct exfat_uni_name *label_out);
525+
int exfat_write_volume_label(struct super_block *sb,
526+
struct exfat_uni_name *label);
520527

521528
/* inode.c */
522529
extern const struct inode_operations exfat_file_inode_operations;

fs/exfat/exfat_raw.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#define BOOTSEC_OLDBPB_LEN 53
8181

8282
#define EXFAT_FILE_NAME_LEN 15
83+
#define EXFAT_VOLUME_LABEL_LEN 11
8384

8485
#define EXFAT_MIN_SECT_SIZE_BITS 9
8586
#define EXFAT_MAX_SECT_SIZE_BITS 12
@@ -159,6 +160,11 @@ struct exfat_dentry {
159160
__le32 start_clu;
160161
__le64 size;
161162
} __packed upcase; /* up-case table directory entry */
163+
struct {
164+
__u8 char_count;
165+
__le16 volume_label[EXFAT_VOLUME_LABEL_LEN];
166+
__u8 reserved[8];
167+
} __packed volume_label; /* volume label directory entry */
162168
struct {
163169
__u8 flags;
164170
__u8 vendor_guid[16];

fs/exfat/file.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,54 @@ static int exfat_ioctl_shutdown(struct super_block *sb, unsigned long arg)
486486
return exfat_force_shutdown(sb, flags);
487487
}
488488

489+
static int exfat_ioctl_get_volume_label(struct super_block *sb, unsigned long arg)
490+
{
491+
int ret;
492+
char label[FSLABEL_MAX] = {0};
493+
struct exfat_uni_name uniname;
494+
495+
ret = exfat_read_volume_label(sb, &uniname);
496+
if (ret < 0)
497+
return ret;
498+
499+
ret = exfat_utf16_to_nls(sb, &uniname, label, uniname.name_len);
500+
if (ret < 0)
501+
return ret;
502+
503+
if (copy_to_user((char __user *)arg, label, ret + 1))
504+
return -EFAULT;
505+
506+
return 0;
507+
}
508+
509+
static int exfat_ioctl_set_volume_label(struct super_block *sb,
510+
unsigned long arg)
511+
{
512+
int ret = 0, lossy;
513+
char label[FSLABEL_MAX];
514+
struct exfat_uni_name uniname;
515+
516+
if (!capable(CAP_SYS_ADMIN))
517+
return -EPERM;
518+
519+
if (copy_from_user(label, (char __user *)arg, FSLABEL_MAX))
520+
return -EFAULT;
521+
522+
memset(&uniname, 0, sizeof(uniname));
523+
if (label[0]) {
524+
ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX,
525+
&uniname, &lossy);
526+
if (ret < 0)
527+
return ret;
528+
else if (lossy & NLS_NAME_LOSSY)
529+
return -EINVAL;
530+
}
531+
532+
uniname.name_len = ret;
533+
534+
return exfat_write_volume_label(sb, &uniname);
535+
}
536+
489537
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
490538
{
491539
struct inode *inode = file_inode(filp);
@@ -500,6 +548,10 @@ long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
500548
return exfat_ioctl_shutdown(inode->i_sb, arg);
501549
case FITRIM:
502550
return exfat_ioctl_fitrim(inode, arg);
551+
case FS_IOC_GETFSLABEL:
552+
return exfat_ioctl_get_volume_label(inode->i_sb, arg);
553+
case FS_IOC_SETFSLABEL:
554+
return exfat_ioctl_set_volume_label(inode->i_sb, arg);
503555
default:
504556
return -ENOTTY;
505557
}

fs/exfat/namei.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ static int exfat_check_max_dentries(struct inode *inode)
300300
* the directory entry index in p_dir is returned on succeeds
301301
* -error code is returned on failure
302302
*/
303-
static int exfat_find_empty_entry(struct inode *inode,
303+
int exfat_find_empty_entry(struct inode *inode,
304304
struct exfat_chain *p_dir, int num_entries,
305305
struct exfat_entry_set_cache *es)
306306
{

0 commit comments

Comments
 (0)