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
12981298 /* Metadata checksum algorithm codes */
12991299 #define EXT4_CRC32C_CHKSUM 1
13001300
1301+#define EXT4_LABEL_MAX 16
1302+
13011303 /*
13021304 * Structure of the super block
13031305 */
@@ -1347,7 +1349,7 @@ struct ext4_super_block { · 43 unmodified lines
13471349 /*60*/ __le32 s_feature_incompat; /* incompatible feature set */
13481350 __le32 s_feature_ro_compat; /* readonly-compatible feature set */
13491351 /*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 */
13511353 /*88*/ char s_last_mounted[64] __nonstring; /* directory where last mounted */
13521354 /*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
13531355 /*
@@ -3094,6 +3096,9 @@ extern int ext4_group_extend(struct super_block *sb, · 1740 unmodified lines
30943096 struct ext4_super_block *es,
30953097 ext4_fsblk_t n_blocks_count);
30963098 extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
3099+extern unsigned int ext4_list_backups(struct super_block *sb,
3100+ unsigned int *three, unsigned int *five,
3101+ unsigned int *seven);
30973102
30983103 /* super.c */
30993104 extern 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
31083113 extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
31093114 extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
31103115 extern int ext4_calculate_overhead(struct super_block *sb);
3116+extern __le32 ext4_superblock_csum(struct super_block *sb,
3117+ struct ext4_super_block *es);
31113118 extern void ext4_superblock_csum_set(struct super_block *sb);
31123119 extern int ext4_alloc_flex_bg_array(struct super_block *sb,
31133120 ext4_group_t ngroup);
fs/ext4/ioctl.c+309−0
@@ -27,6 +27,248 @@ · 26 unmodified lines
2727 #include "fsmap.h"
2828 #include <trace/events/ext4.h>
2929
30+typedef 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+ */
37+static 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+
45+static
46+int 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);
80+out_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+ */
94+static 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+
163+out_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+ */
178+static
179+int 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+ }
256+next_grp:
257+ grp = ext4_list_backups(sb, &three, &five, &seven);
258+ }
259+
260+out_journal:
261+ if (handle) {
262+ ret = ext4_journal_stop(handle);
263+ if (ret && !err)
264+ err = ret;
265+ }
266+out:
267+ clear_bit_unlock(EXT4_FLAGS_RESIZING, &sbi->s_ext4_flags);
268+ smp_mb__after_atomic();
269+ return err ? err : 0;
270+}
271+
30272 /**
31273 * Swap memory between @a and @b for @len bytes.
32274 *
@@ -847,6 +1089,64 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg) · 814 unmodified lines
8471089 return err;
8481090 }
8491091
1092+static 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+
1129+static 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+
8501150 static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
8511151 {
8521152 struct inode *inode = file_inode(filp);
@@ -1261,6 +1561,13 @@ resizefs_out: · 408 unmodified lines
12611561 case EXT4_IOC_CHECKPOINT:
12621562 return ext4_ioctl_checkpoint(filp, arg);
12631563
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+
12641571 default:
12651572 return -ENOTTY;
12661573 }
@@ -1336,6 +1643,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) · 69 unmodified lines
13361643 case EXT4_IOC_GETSTATE:
13371644 case EXT4_IOC_GET_ES_CACHE:
13381645 case EXT4_IOC_CHECKPOINT:
1646+ case FS_IOC_GETFSLABEL:
1647+ case FS_IOC_SETFSLABEL:
13391648 break;
13401649 default:
13411650 return -ENOIOCTLCMD;
fs/ext4/resize.c+15−4
@@ -717,12 +717,23 @@ out: · 716 unmodified lines
717717 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
718718 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
719719 */
720−static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
721− unsigned *five, unsigned *seven)
720+unsigned int ext4_list_backups(struct super_block *sb, unsigned int *three,
721+ unsigned int *five, unsigned int *seven)
722722 {
723− unsigned *min = three;
723+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;
724+ unsigned int *min = three;
724725 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+ }
726737
727738 if (!ext4_has_feature_sparse_super(sb)) {
728739 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
279279 return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
280280 }
281281
282−static __le32 ext4_superblock_csum(struct super_block *sb,
283− struct ext4_super_block *es)
282+__le32 ext4_superblock_csum(struct super_block *sb,
283+ struct ext4_super_block *es)
284284 {
285285 struct ext4_sb_info *sbi = EXT4_SB(sb);
286286 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
28372837 __entry->end)
28382838 );
28392839
2840+TRACE_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+
28402863 #endif /* _TRACE_EXT4_H */
28412864
28422865 /* 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