| @@ -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 | |
| | 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 | |
| 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 | |
| | 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 | |
| 850 | static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 1150 | static 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; |