History

ext4: implement support for get/set fs label

Implement support for FS_IOC_GETFSLABEL and FS_IOC_SETFSLABEL ioctls for online reading and setting of file system label. ext4_ioctl_getlabel() is simple, just get the label from the primary superblock. This might not be the first sb on the file system if 'sb=' mount option is used. In ext4_ioctl_setlabel() we update what ext4 currently views as a primary superblock and then proceed to update backup superblocks. There are two caveats: - the primary superblock might not be the first superblock and so it might not be the one used by userspace tools if read directly off the disk. - because the primary superblock might not be the first superblock we potentialy have to update it as part of backup superblock update. However the first sb location is a bit more complicated than the rest so we have to account for that. The superblock modification is created generic enough so the infrastructure can be used for other potential superblock modification operations, such as chaning UUID. Tested with generic/492 with various configurations. I also checked the behavior with 'sb=' mount options, including very large file systems with and without sparse_super/sparse_super2.

Lukas Czerner committed 4 years ago

Commit bbc605c1 parent 4c1bd5a5 files · +357 −7Browse files

Unified · Split

5 files
  1. fs/ext4/ext4.h +8 −1
  2. fs/ext4/ioctl.c +309 −0
  3. fs/ext4/resize.c +15 −4
  4. fs/ext4/super.c +2 −2
  5. include/trace/events/ext4.h +23 −0
fs/ext4/ext4.h+8−1
@@ -1298,6 +1298,8 @@ extern void ext4_set_bits(void *bm, int cur, int len); · 1297 unmodified lines
1298/* Metadata checksum algorithm codes */1298/* Metadata checksum algorithm codes */
1299#define EXT4_CRC32C_CHKSUM 11299#define EXT4_CRC32C_CHKSUM 1
1300 1300
1301#define EXT4_LABEL_MAX 16
1302
1301/*1303/*
1302 * Structure of the super block1304 * Structure of the super block
1303 */1305 */
@@ -1347,7 +1349,7 @@ struct ext4_super_block { · 43 unmodified lines
1347/*60*/ __le32 s_feature_incompat; /* incompatible feature set */1349/*60*/ __le32 s_feature_incompat; /* incompatible feature set */
1348 __le32 s_feature_ro_compat; /* readonly-compatible feature set */1350 __le32 s_feature_ro_compat; /* readonly-compatible feature set */
1349/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */1351/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */
1350/*78*/ char s_volume_name[16]; /* volume name */1352/*78*/ char s_volume_name[EXT4_LABEL_MAX]; /* volume name */
1351/*88*/ char s_last_mounted[64] __nonstring; /* directory where last mounted */1353/*88*/ char s_last_mounted[64] __nonstring; /* directory where last mounted */
1352/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */1354/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
1353 /*1355 /*
@@ -3094,6 +3096,9 @@ extern int ext4_group_extend(struct super_block *sb, · 1740 unmodified lines
3094 struct ext4_super_block *es,3096 struct ext4_super_block *es,
3095 ext4_fsblk_t n_blocks_count);3097 ext4_fsblk_t n_blocks_count);
3096extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);3098extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
3099extern unsigned int ext4_list_backups(struct super_block *sb,
3100 unsigned int *three, unsigned int *five,
3101 unsigned int *seven);
3097 3102
3098/* super.c */3103/* super.c */
3099extern struct buffer_head *ext4_sb_bread(struct super_block *sb,3104extern struct buffer_head *ext4_sb_bread(struct super_block *sb,
@@ -3108,6 +3113,8 @@ extern int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait); · 8 unmodified lines
3108extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);3113extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
3109extern int ext4_seq_options_show(struct seq_file *seq, void *offset);3114extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
3110extern int ext4_calculate_overhead(struct super_block *sb);3115extern int ext4_calculate_overhead(struct super_block *sb);
3116extern __le32 ext4_superblock_csum(struct super_block *sb,
3117 struct ext4_super_block *es);
3111extern void ext4_superblock_csum_set(struct super_block *sb);3118extern void ext4_superblock_csum_set(struct super_block *sb);
3112extern int ext4_alloc_flex_bg_array(struct super_block *sb,3119extern int ext4_alloc_flex_bg_array(struct super_block *sb,
3113 ext4_group_t ngroup);3120 ext4_group_t ngroup);
fs/ext4/ioctl.c+309−0
@@ -27,6 +27,248 @@ · 26 unmodified lines
27#include "fsmap.h"27#include "fsmap.h"
28#include <trace/events/ext4.h>28#include <trace/events/ext4.h>
29 29
30typedef void ext4_update_sb_callback(struct ext4_super_block *es,
31 const void *arg);
32
33/*
34 * Superblock modification callback function for changing file system
35 * label
36 */
37static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg)
38{
39 /* Sanity check, this should never happen */
40 BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX);
41
42 memcpy(es->s_volume_name, (char *)arg, EXT4_LABEL_MAX);
43}
44
45static
46int ext4_update_primary_sb(struct super_block *sb, handle_t *handle,
47 ext4_update_sb_callback func,
48 const void *arg)
49{
50 int err = 0;
51 struct ext4_sb_info *sbi = EXT4_SB(sb);
52 struct buffer_head *bh = sbi->s_sbh;
53 struct ext4_super_block *es = sbi->s_es;
54
55 trace_ext4_update_sb(sb, bh->b_blocknr, 1);
56
57 BUFFER_TRACE(bh, "get_write_access");
58 err = ext4_journal_get_write_access(handle, sb,
59 bh,
60 EXT4_JTR_NONE);
61 if (err)
62 goto out_err;
63
64 lock_buffer(bh);
65 func(es, arg);
66 ext4_superblock_csum_set(sb);
67 unlock_buffer(bh);
68
69 if (buffer_write_io_error(bh) || !buffer_uptodate(bh)) {
70 ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to "
71 "superblock detected");
72 clear_buffer_write_io_error(bh);
73 set_buffer_uptodate(bh);
74 }
75
76 err = ext4_handle_dirty_metadata(handle, NULL, bh);
77 if (err)
78 goto out_err;
79 err = sync_dirty_buffer(bh);
80out_err:
81 ext4_std_error(sb, err);
82 return err;
83}
84
85/*
86 * Update one backup superblock in the group 'grp' using the callback
87 * function 'func' and argument 'arg'. If the handle is NULL the
88 * modification is not journalled.
89 *
90 * Returns: 0 when no modification was done (no superblock in the group)
91 * 1 when the modification was successful
92 * <0 on error
93 */
94static int ext4_update_backup_sb(struct super_block *sb,
95 handle_t *handle, ext4_group_t grp,
96 ext4_update_sb_callback func, const void *arg)
97{
98 int err = 0;
99 ext4_fsblk_t sb_block;
100 struct buffer_head *bh;
101 unsigned long offset = 0;
102 struct ext4_super_block *es;
103
104 if (!ext4_bg_has_super(sb, grp))
105 return 0;
106
107 /*
108 * For the group 0 there is always 1k padding, so we have
109 * either adjust offset, or sb_block depending on blocksize
110 */
111 if (grp == 0) {
112 sb_block = 1 * EXT4_MIN_BLOCK_SIZE;
113 offset = do_div(sb_block, sb->s_blocksize);
114 } else {
115 sb_block = ext4_group_first_block_no(sb, grp);
116 offset = 0;
117 }
118
119 trace_ext4_update_sb(sb, sb_block, handle ? 1 : 0);
120
121 bh = ext4_sb_bread(sb, sb_block, 0);
122 if (IS_ERR(bh))
123 return PTR_ERR(bh);
124
125 if (handle) {
126 BUFFER_TRACE(bh, "get_write_access");
127 err = ext4_journal_get_write_access(handle, sb,
128 bh,
129 EXT4_JTR_NONE);
130 if (err)
131 goto out_bh;
132 }
133
134 es = (struct ext4_super_block *) (bh->b_data + offset);
135 lock_buffer(bh);
136 if (ext4_has_metadata_csum(sb) &&
137 es->s_checksum != ext4_superblock_csum(sb, es)) {
138 ext4_msg(sb, KERN_ERR, "Invalid checksum for backup "
139 "superblock %llu\n", sb_block);
140 unlock_buffer(bh);
141 err = -EFSBADCRC;
142 goto out_bh;
143 }
144 func(es, arg);
145 if (ext4_has_metadata_csum(sb))
146 es->s_checksum = ext4_superblock_csum(sb, es);
147 set_buffer_uptodate(bh);
148 unlock_buffer(bh);
149
150 if (err)
151 goto out_bh;
152
153 if (handle) {
154 err = ext4_handle_dirty_metadata(handle, NULL, bh);
155 if (err)
156 goto out_bh;
157 } else {
158 BUFFER_TRACE(bh, "marking dirty");
159 mark_buffer_dirty(bh);
160 }
161 err = sync_dirty_buffer(bh);
162
163out_bh:
164 brelse(bh);
165 ext4_std_error(sb, err);
166 return (err) ? err : 1;
167}
168
169/*
170 * Update primary and backup superblocks using the provided function
171 * func and argument arg.
172 *
173 * Only the primary superblock and at most two backup superblock
174 * modifications are journalled; the rest is modified without journal.
175 * This is safe because e2fsck will re-write them if there is a problem,
176 * and we're very unlikely to ever need more than two backups.
177 */
178static
179int ext4_update_superblocks_fn(struct super_block *sb,
180 ext4_update_sb_callback func,
181 const void *arg)
182{
183 handle_t *handle;
184 ext4_group_t ngroups;
185 unsigned int three = 1;
186 unsigned int five = 5;
187 unsigned int seven = 7;
188 int err = 0, ret, i;
189 ext4_group_t grp, primary_grp;
190 struct ext4_sb_info *sbi = EXT4_SB(sb);
191
192 /*
193 * We can't update superblocks while the online resize is running
194 */
195 if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING,
196 &sbi->s_ext4_flags)) {
197 ext4_msg(sb, KERN_ERR, "Can't modify superblock while"
198 "performing online resize");
199 return -EBUSY;
200 }
201
202 /*
203 * We're only going to update primary superblock and two
204 * backup superblocks in this transaction.
205 */
206 handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 3);
207 if (IS_ERR(handle)) {
208 err = PTR_ERR(handle);
209 goto out;
210 }
211
212 /* Update primary superblock */
213 err = ext4_update_primary_sb(sb, handle, func, arg);
214 if (err) {
215 ext4_msg(sb, KERN_ERR, "Failed to update primary "
216 "superblock");
217 goto out_journal;
218 }
219
220 primary_grp = ext4_get_group_number(sb, sbi->s_sbh->b_blocknr);
221 ngroups = ext4_get_groups_count(sb);
222
223 /*
224 * Update backup superblocks. We have to start from group 0
225 * because it might not be where the primary superblock is
226 * if the fs is mounted with -o sb=<backup_sb_block>
227 */
228 i = 0;
229 grp = 0;
230 while (grp < ngroups) {
231 /* Skip primary superblock */
232 if (grp == primary_grp)
233 goto next_grp;
234
235 ret = ext4_update_backup_sb(sb, handle, grp, func, arg);
236 if (ret < 0) {
237 /* Ignore bad checksum; try to update next sb */
238 if (ret == -EFSBADCRC)
239 goto next_grp;
240 err = ret;
241 goto out_journal;
242 }
243
244 i += ret;
245 if (handle && i > 1) {
246 /*
247 * We're only journalling primary superblock and
248 * two backup superblocks; the rest is not
249 * journalled.
250 */
251 err = ext4_journal_stop(handle);
252 if (err)
253 goto out;
254 handle = NULL;
255 }
256next_grp:
257 grp = ext4_list_backups(sb, &three, &five, &seven);
258 }
259
260out_journal:
261 if (handle) {
262 ret = ext4_journal_stop(handle);
263 if (ret && !err)
264 err = ret;
265 }
266out:
267 clear_bit_unlock(EXT4_FLAGS_RESIZING, &sbi->s_ext4_flags);
268 smp_mb__after_atomic();
269 return err ? err : 0;
270}
271
30/**272/**
31 * Swap memory between @a and @b for @len bytes.273 * Swap memory between @a and @b for @len bytes.
32 *274 *
@@ -847,6 +1089,64 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg) · 814 unmodified lines
847 return err;1089 return err;
848}1090}
849 1091
1092static int ext4_ioctl_setlabel(struct file *filp, const char __user *user_label)
1093{
1094 size_t len;
1095 int ret = 0;
1096 char new_label[EXT4_LABEL_MAX + 1];
1097 struct super_block *sb = file_inode(filp)->i_sb;
1098
1099 if (!capable(CAP_SYS_ADMIN))
1100 return -EPERM;
1101
1102 /*
1103 * Copy the maximum length allowed for ext4 label with one more to
1104 * find the required terminating null byte in order to test the
1105 * label length. The on disk label doesn't need to be null terminated.
1106 */
1107 if (copy_from_user(new_label, user_label, EXT4_LABEL_MAX + 1))
1108 return -EFAULT;
1109
1110 len = strnlen(new_label, EXT4_LABEL_MAX + 1);
1111 if (len > EXT4_LABEL_MAX)
1112 return -EINVAL;
1113
1114 /*
1115 * Clear the buffer after the new label
1116 */
1117 memset(new_label + len, 0, EXT4_LABEL_MAX - len);
1118
1119 ret = mnt_want_write_file(filp);
1120 if (ret)
1121 return ret;
1122
1123 ret = ext4_update_superblocks_fn(sb, ext4_sb_setlabel, new_label);
1124
1125 mnt_drop_write_file(filp);
1126 return ret;
1127}
1128
1129static int ext4_ioctl_getlabel(struct ext4_sb_info *sbi, char __user *user_label)
1130{
1131 char label[EXT4_LABEL_MAX + 1];
1132
1133 /*
1134 * EXT4_LABEL_MAX must always be smaller than FSLABEL_MAX because
1135 * FSLABEL_MAX must include terminating null byte, while s_volume_name
1136 * does not have to.
1137 */
1138 BUILD_BUG_ON(EXT4_LABEL_MAX >= FSLABEL_MAX);
1139
1140 memset(label, 0, sizeof(label));
1141 lock_buffer(sbi->s_sbh);
1142 strncpy(label, sbi->s_es->s_volume_name, EXT4_LABEL_MAX);
1143 unlock_buffer(sbi->s_sbh);
1144
1145 if (copy_to_user(user_label, label, sizeof(label)))
1146 return -EFAULT;
1147 return 0;
1148}
1149
850static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)1150static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
851{1151{
852 struct inode *inode = file_inode(filp);1152 struct inode *inode = file_inode(filp);
@@ -1261,6 +1561,13 @@ resizefs_out: · 408 unmodified lines
1261 case EXT4_IOC_CHECKPOINT:1561 case EXT4_IOC_CHECKPOINT:
1262 return ext4_ioctl_checkpoint(filp, arg);1562 return ext4_ioctl_checkpoint(filp, arg);
1263 1563
1564 case FS_IOC_GETFSLABEL:
1565 return ext4_ioctl_getlabel(EXT4_SB(sb), (void __user *)arg);
1566
1567 case FS_IOC_SETFSLABEL:
1568 return ext4_ioctl_setlabel(filp,
1569 (const void __user *)arg);
1570
1264 default:1571 default:
1265 return -ENOTTY;1572 return -ENOTTY;
1266 }1573 }
@@ -1336,6 +1643,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) · 69 unmodified lines
1336 case EXT4_IOC_GETSTATE:1643 case EXT4_IOC_GETSTATE:
1337 case EXT4_IOC_GET_ES_CACHE:1644 case EXT4_IOC_GET_ES_CACHE:
1338 case EXT4_IOC_CHECKPOINT:1645 case EXT4_IOC_CHECKPOINT:
1646 case FS_IOC_GETFSLABEL:
1647 case FS_IOC_SETFSLABEL:
1339 break;1648 break;
1340 default:1649 default:
1341 return -ENOIOCTLCMD;1650 return -ENOIOCTLCMD;
fs/ext4/resize.c+15−4
@@ -717,12 +717,23 @@ out: · 716 unmodified lines
717 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...717 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
718 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...718 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
719 */719 */
720static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,720unsigned int ext4_list_backups(struct super_block *sb, unsigned int *three,
721 unsigned *five, unsigned *seven)721 unsigned int *five, unsigned int *seven)
722{722{
723 unsigned *min = three;723 struct ext4_super_block *es = EXT4_SB(sb)->s_es;
724 unsigned int *min = three;
724 int mult = 3;725 int mult = 3;
725 unsigned ret;726 unsigned int ret;
727
728 if (ext4_has_feature_sparse_super2(sb)) {
729 do {
730 if (*min > 2)
731 return UINT_MAX;
732 ret = le32_to_cpu(es->s_backup_bgs[*min - 1]);
733 *min += 1;
734 } while (!ret);
735 return ret;
736 }
726 737
727 if (!ext4_has_feature_sparse_super(sb)) {738 if (!ext4_has_feature_sparse_super(sb)) {
728 ret = *min;739 ret = *min;
fs/ext4/super.c+2−2
@@ -279,8 +279,8 @@ static int ext4_verify_csum_type(struct super_block *sb, · 278 unmodified lines
279 return es->s_checksum_type == EXT4_CRC32C_CHKSUM;279 return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
280}280}
281 281
282static __le32 ext4_superblock_csum(struct super_block *sb,282__le32 ext4_superblock_csum(struct super_block *sb,
283 struct ext4_super_block *es)283 struct ext4_super_block *es)
284{284{
285 struct ext4_sb_info *sbi = EXT4_SB(sb);285 struct ext4_sb_info *sbi = EXT4_SB(sb);
286 int offset = offsetof(struct ext4_super_block, s_checksum);286 int offset = offsetof(struct ext4_super_block, s_checksum);
include/trace/events/ext4.h+23−0
@@ -2837,6 +2837,29 @@ TRACE_EVENT(ext4_fc_track_range, · 2836 unmodified lines
2837 __entry->end)2837 __entry->end)
2838 );2838 );
2839 2839
2840TRACE_EVENT(ext4_update_sb,
2841 TP_PROTO(struct super_block *sb, ext4_fsblk_t fsblk,
2842 unsigned int flags),
2843
2844 TP_ARGS(sb, fsblk, flags),
2845
2846 TP_STRUCT__entry(
2847 __field(dev_t, dev)
2848 __field(ext4_fsblk_t, fsblk)
2849 __field(unsigned int, flags)
2850 ),
2851
2852 TP_fast_assign(
2853 __entry->dev = sb->s_dev;
2854 __entry->fsblk = fsblk;
2855 __entry->flags = flags;
2856 ),
2857
2858 TP_printk("dev %d,%d fsblk %llu flags %u",
2859 MAJOR(__entry->dev), MINOR(__entry->dev),
2860 __entry->fsblk, __entry->flags)
2861);
2862
2840#endif /* _TRACE_EXT4_H */2863#endif /* _TRACE_EXT4_H */
2841 2864
2842/* This part must be outside protection */2865/* This part must be outside protection */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t