Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+170 -47
+10 -10
fs/exfat/dir.c
··· 287 287 288 288 mutex_unlock(&EXFAT_SB(sb)->s_lock); 289 289 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)) 291 291 goto out; 292 292 ctx->pos = cpos; 293 293 goto get_new; ··· 359 359 if (ep->type == EXFAT_VOLUME) 360 360 return TYPE_VOLUME; 361 361 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) 363 363 return TYPE_DIR; 364 364 return TYPE_FILE; 365 365 } ··· 410 410 ep->type = EXFAT_VOLUME; 411 411 } else if (type == TYPE_DIR) { 412 412 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); 414 414 } else if (type == TYPE_FILE) { 415 415 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); 417 417 } 418 418 } 419 419 420 420 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) 423 422 { 424 423 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; 426 428 ep->dentry.stream.start_clu = cpu_to_le32(start_clu); 427 429 ep->dentry.stream.valid_size = cpu_to_le64(size); 428 430 ep->dentry.stream.size = cpu_to_le64(size); ··· 490 488 if (!ep) 491 489 return -EIO; 492 490 493 - exfat_init_stream_entry(ep, 494 - (type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN, 495 - start_clu, size); 491 + exfat_init_stream_entry(ep, start_clu, size); 496 492 exfat_update_bh(bh, IS_DIRSYNC(inode)); 497 493 brelse(bh); 498 494
+8 -6
fs/exfat/exfat_fs.h
··· 234 234 discard:1, /* Issue discard requests on deletions */ 235 235 keep_last_dots:1; /* Keep trailing periods in paths */ 236 236 int time_offset; /* Offset of timestamps from UTC (in minutes) */ 237 + /* Support creating zero-size directory, default: false */ 238 + bool zero_size_dir; 237 239 }; 238 240 239 241 /* ··· 359 357 static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, 360 358 unsigned short attr, mode_t mode) 361 359 { 362 - if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) 360 + if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR)) 363 361 mode &= ~0222; 364 362 365 - if (attr & ATTR_SUBDIR) 363 + if (attr & EXFAT_ATTR_SUBDIR) 366 364 return (mode & ~sbi->options.fs_dmask) | S_IFDIR; 367 365 368 366 return (mode & ~sbi->options.fs_fmask) | S_IFREG; ··· 374 372 unsigned short attr = EXFAT_I(inode)->attr; 375 373 376 374 if (S_ISDIR(inode->i_mode)) 377 - attr |= ATTR_SUBDIR; 375 + attr |= EXFAT_ATTR_SUBDIR; 378 376 if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222)) 379 - attr |= ATTR_READONLY; 377 + attr |= EXFAT_ATTR_READONLY; 380 378 return attr; 381 379 } 382 380 383 381 static inline void exfat_save_attr(struct inode *inode, unsigned short attr) 384 382 { 385 383 if (exfat_mode_can_hold_ro(inode)) 386 - EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY); 384 + EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY); 387 385 else 388 - EXFAT_I(inode)->attr = attr & ATTR_RWMASK; 386 + EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK; 389 387 } 390 388 391 389 static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
+9 -8
fs/exfat/exfat_raw.h
··· 64 64 #define CS_DEFAULT 2 65 65 66 66 /* 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 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 73 74 - #define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ 75 - ATTR_SUBDIR | ATTR_ARCHIVE) 74 + #define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \ 75 + EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \ 76 + EXFAT_ATTR_ARCHIVE) 76 77 77 78 #define BOOTSEC_JUMP_BOOT_LEN 3 78 79 #define BOOTSEC_FS_NAME_LEN 8
+96 -1
fs/exfat/file.c
··· 8 8 #include <linux/cred.h> 9 9 #include <linux/buffer_head.h> 10 10 #include <linux/blkdev.h> 11 + #include <linux/fsnotify.h> 12 + #include <linux/security.h> 13 + #include <linux/msdos_fs.h> 11 14 12 15 #include "exfat_raw.h" 13 16 #include "exfat_fs.h" ··· 147 144 } 148 145 149 146 if (ei->type == TYPE_FILE) 150 - ei->attr |= ATTR_ARCHIVE; 147 + ei->attr |= EXFAT_ATTR_ARCHIVE; 151 148 152 149 /* 153 150 * update the directory entry ··· 318 315 return error; 319 316 } 320 317 318 + /* 319 + * modified ioctls from fat/file.c by Welmer Almesberger 320 + */ 321 + static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr) 322 + { 323 + u32 attr; 324 + 325 + inode_lock_shared(inode); 326 + attr = exfat_make_attr(inode); 327 + inode_unlock_shared(inode); 328 + 329 + return put_user(attr, user_attr); 330 + } 331 + 332 + static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) 333 + { 334 + struct inode *inode = file_inode(file); 335 + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); 336 + int is_dir = S_ISDIR(inode->i_mode); 337 + u32 attr, oldattr; 338 + struct iattr ia; 339 + int err; 340 + 341 + err = get_user(attr, user_attr); 342 + if (err) 343 + goto out; 344 + 345 + err = mnt_want_write_file(file); 346 + if (err) 347 + goto out; 348 + inode_lock(inode); 349 + 350 + oldattr = exfat_make_attr(inode); 351 + 352 + /* 353 + * Mask attributes so we don't set reserved fields. 354 + */ 355 + attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | 356 + EXFAT_ATTR_ARCHIVE); 357 + attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0); 358 + 359 + /* Equivalent to a chmod() */ 360 + ia.ia_valid = ATTR_MODE | ATTR_CTIME; 361 + ia.ia_ctime = current_time(inode); 362 + if (is_dir) 363 + ia.ia_mode = exfat_make_mode(sbi, attr, 0777); 364 + else 365 + ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111)); 366 + 367 + /* The root directory has no attributes */ 368 + if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) { 369 + err = -EINVAL; 370 + goto out_unlock_inode; 371 + } 372 + 373 + if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) && 374 + !capable(CAP_LINUX_IMMUTABLE)) { 375 + err = -EPERM; 376 + goto out_unlock_inode; 377 + } 378 + 379 + /* 380 + * The security check is questionable... We single 381 + * out the RO attribute for checking by the security 382 + * module, just because it maps to a file mode. 383 + */ 384 + err = security_inode_setattr(file_mnt_idmap(file), 385 + file->f_path.dentry, &ia); 386 + if (err) 387 + goto out_unlock_inode; 388 + 389 + /* This MUST be done before doing anything irreversible... */ 390 + err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia); 391 + if (err) 392 + goto out_unlock_inode; 393 + 394 + fsnotify_change(file->f_path.dentry, ia.ia_valid); 395 + 396 + exfat_save_attr(inode, attr); 397 + mark_inode_dirty(inode); 398 + out_unlock_inode: 399 + inode_unlock(inode); 400 + mnt_drop_write_file(file); 401 + out: 402 + return err; 403 + } 404 + 321 405 static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg) 322 406 { 323 407 struct fstrim_range range; ··· 435 345 long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 436 346 { 437 347 struct inode *inode = file_inode(filp); 348 + u32 __user *user_attr = (u32 __user *)arg; 438 349 439 350 switch (cmd) { 351 + case FAT_IOCTL_GET_ATTRIBUTES: 352 + return exfat_ioctl_get_attributes(inode, user_attr); 353 + case FAT_IOCTL_SET_ATTRIBUTES: 354 + return exfat_ioctl_set_attributes(filp, user_attr); 440 355 case FITRIM: 441 356 return exfat_ioctl_fitrim(inode, arg); 442 357 default:
+3 -3
fs/exfat/inode.c
··· 400 400 if (err < len) 401 401 exfat_write_failed(mapping, pos+len); 402 402 403 - if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) { 403 + if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) { 404 404 inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); 405 - ei->attr |= ATTR_ARCHIVE; 405 + ei->attr |= EXFAT_ATTR_ARCHIVE; 406 406 mark_inode_dirty(inode); 407 407 } 408 408 ··· 550 550 inode_inc_iversion(inode); 551 551 inode->i_generation = get_random_u32(); 552 552 553 - if (info->attr & ATTR_SUBDIR) { /* directory */ 553 + if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */ 554 554 inode->i_generation &= ~1; 555 555 inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); 556 556 inode->i_op = &exfat_dir_inode_operations;
+35 -17
fs/exfat/namei.c
··· 351 351 if (exfat_check_max_dentries(inode)) 352 352 return -ENOSPC; 353 353 354 - /* we trust p_dir->size regardless of FAT type */ 355 - if (exfat_find_last_cluster(sb, p_dir, &last_clu)) 356 - return -EIO; 357 - 358 354 /* 359 355 * Allocate new cluster to this directory 360 356 */ 361 - exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); 357 + if (ei->start_clu != EXFAT_EOF_CLUSTER) { 358 + /* we trust p_dir->size regardless of FAT type */ 359 + if (exfat_find_last_cluster(sb, p_dir, &last_clu)) 360 + return -EIO; 361 + 362 + exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); 363 + } else { 364 + /* This directory is empty */ 365 + exfat_chain_set(&clu, EXFAT_EOF_CLUSTER, 0, 366 + ALLOC_NO_FAT_CHAIN); 367 + } 362 368 363 369 /* allocate a cluster */ 364 370 ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode)); ··· 373 367 374 368 if (exfat_zeroed_cluster(inode, clu.dir)) 375 369 return -EIO; 370 + 371 + if (ei->start_clu == EXFAT_EOF_CLUSTER) { 372 + ei->start_clu = clu.dir; 373 + p_dir->dir = clu.dir; 374 + } 376 375 377 376 /* append to the FAT chain */ 378 377 if (clu.flags != p_dir->flags) { ··· 518 507 goto out; 519 508 } 520 509 521 - if (type == TYPE_DIR) { 510 + if (type == TYPE_DIR && !sbi->options.zero_size_dir) { 522 511 ret = exfat_alloc_new_dir(inode, &clu); 523 512 if (ret) 524 513 goto out; ··· 545 534 info->type = type; 546 535 547 536 if (type == TYPE_FILE) { 548 - info->attr = ATTR_ARCHIVE; 537 + info->attr = EXFAT_ATTR_ARCHIVE; 549 538 info->start_clu = EXFAT_EOF_CLUSTER; 550 539 info->size = 0; 551 540 info->num_subdirs = 0; 552 541 } else { 553 - info->attr = ATTR_SUBDIR; 554 - info->start_clu = start_clu; 542 + info->attr = EXFAT_ATTR_SUBDIR; 543 + if (sbi->options.zero_size_dir) 544 + info->start_clu = EXFAT_EOF_CLUSTER; 545 + else 546 + info->start_clu = start_clu; 555 547 info->size = clu_size; 556 548 info->num_subdirs = EXFAT_MIN_SUBDIR; 557 549 } ··· 660 646 info->type = exfat_get_entry_type(ep); 661 647 info->attr = le16_to_cpu(ep->dentry.file.attr); 662 648 info->size = le64_to_cpu(ep2->dentry.stream.valid_size); 663 - if ((info->type == TYPE_FILE) && (info->size == 0)) { 649 + if (info->size == 0) { 664 650 info->flags = ALLOC_NO_FAT_CHAIN; 665 651 info->start_clu = EXFAT_EOF_CLUSTER; 666 652 } else { ··· 903 889 904 890 dentries_per_clu = sbi->dentries_per_clu; 905 891 892 + if (p_dir->dir == EXFAT_EOF_CLUSTER) 893 + return 0; 894 + 906 895 exfat_chain_dup(&clu, p_dir); 907 896 908 897 while (clu.dir != EXFAT_EOF_CLUSTER) { ··· 1050 1033 1051 1034 *epnew = *epold; 1052 1035 if (exfat_get_entry_type(epnew) == TYPE_FILE) { 1053 - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); 1054 - ei->attr |= ATTR_ARCHIVE; 1036 + epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); 1037 + ei->attr |= EXFAT_ATTR_ARCHIVE; 1055 1038 } 1056 1039 exfat_update_bh(new_bh, sync); 1057 1040 brelse(old_bh); ··· 1082 1065 ei->entry = newentry; 1083 1066 } else { 1084 1067 if (exfat_get_entry_type(epold) == TYPE_FILE) { 1085 - epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); 1086 - ei->attr |= ATTR_ARCHIVE; 1068 + epold->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); 1069 + ei->attr |= EXFAT_ATTR_ARCHIVE; 1087 1070 } 1088 1071 exfat_update_bh(old_bh, sync); 1089 1072 brelse(old_bh); ··· 1131 1114 1132 1115 *epnew = *epmov; 1133 1116 if (exfat_get_entry_type(epnew) == TYPE_FILE) { 1134 - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); 1135 - ei->attr |= ATTR_ARCHIVE; 1117 + epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE); 1118 + ei->attr |= EXFAT_ATTR_ARCHIVE; 1136 1119 } 1137 1120 exfat_update_bh(new_bh, IS_DIRSYNC(inode)); 1138 1121 brelse(mov_bh); ··· 1273 1256 } 1274 1257 1275 1258 /* Free the clusters if new_inode is a dir(as if exfat_rmdir) */ 1276 - if (new_entry_type == TYPE_DIR) { 1259 + if (new_entry_type == TYPE_DIR && 1260 + new_ei->start_clu != EXFAT_EOF_CLUSTER) { 1277 1261 /* new_ei, new_clu_to_free */ 1278 1262 struct exfat_chain new_clu_to_free; 1279 1263
+9 -2
fs/exfat/super.c
··· 165 165 seq_puts(m, ",sys_tz"); 166 166 else if (opts->time_offset) 167 167 seq_printf(m, ",time_offset=%d", opts->time_offset); 168 + if (opts->zero_size_dir) 169 + seq_puts(m, ",zero_size_dir"); 168 170 return 0; 169 171 } 170 172 ··· 211 209 Opt_keep_last_dots, 212 210 Opt_sys_tz, 213 211 Opt_time_offset, 212 + Opt_zero_size_dir, 214 213 215 214 /* Deprecated options */ 216 215 Opt_utf8, ··· 240 237 fsparam_flag("keep_last_dots", Opt_keep_last_dots), 241 238 fsparam_flag("sys_tz", Opt_sys_tz), 242 239 fsparam_s32("time_offset", Opt_time_offset), 240 + fsparam_flag("zero_size_dir", Opt_zero_size_dir), 243 241 __fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated, 244 242 NULL), 245 243 __fsparam(NULL, "debug", Opt_debug, fs_param_deprecated, ··· 309 305 return -EINVAL; 310 306 opts->time_offset = result.int_32; 311 307 break; 308 + case Opt_zero_size_dir: 309 + opts->zero_size_dir = true; 310 + break; 312 311 case Opt_utf8: 313 312 case Opt_debug: 314 313 case Opt_namecase: ··· 367 360 inode->i_gid = sbi->options.fs_gid; 368 361 inode_inc_iversion(inode); 369 362 inode->i_generation = 0; 370 - inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777); 363 + inode->i_mode = exfat_make_mode(sbi, EXFAT_ATTR_SUBDIR, 0777); 371 364 inode->i_op = &exfat_dir_inode_operations; 372 365 inode->i_fop = &exfat_dir_operations; 373 366 ··· 376 369 ei->i_size_aligned = i_size_read(inode); 377 370 ei->i_size_ondisk = i_size_read(inode); 378 371 379 - exfat_save_attr(inode, ATTR_SUBDIR); 372 + exfat_save_attr(inode, EXFAT_ATTR_SUBDIR); 380 373 ei->i_crtime = simple_inode_init_ts(inode); 381 374 exfat_truncate_inode_atime(inode); 382 375 return 0;