Skip to content

Commit 6493792

Browse files
zhangyi089tytso
authored andcommitted
ext4: convert symlink external data block mapping to bdev
Symlink's external data block is one kind of metadata block, and now that almost all ext4 metadata block's page cache (e.g. directory blocks, quota blocks...) belongs to bdev backing inode except the symlink. It is essentially worked in data=journal mode like other regular file's data block because probably in order to make it simple for generic VFS code handling symlinks or some other historical reasons, but the logic of creating external data block in ext4_symlink() is complicated. and it also make things confused if user do not want to let the filesystem worked in data=journal mode. This patch convert the final exceptional case and make things clean, move the mapping of the symlink's external data block to bdev like any other metadata block does. Signed-off-by: Zhang Yi <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9558cf1 commit 6493792

File tree

3 files changed

+100
-83
lines changed

3 files changed

+100
-83
lines changed

fs/ext4/inode.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,7 @@ void ext4_evict_inode(struct inode *inode)
199199
*/
200200
if (inode->i_ino != EXT4_JOURNAL_INO &&
201201
ext4_should_journal_data(inode) &&
202-
(S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
203-
inode->i_data.nrpages) {
202+
S_ISREG(inode->i_mode) && inode->i_data.nrpages) {
204203
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
205204
tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
206205

@@ -2958,8 +2957,7 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
29582957

29592958
index = pos >> PAGE_SHIFT;
29602959

2961-
if (ext4_nonda_switch(inode->i_sb) || S_ISLNK(inode->i_mode) ||
2962-
ext4_verity_in_progress(inode)) {
2960+
if (ext4_nonda_switch(inode->i_sb) || ext4_verity_in_progress(inode)) {
29632961
*fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
29642962
return ext4_write_begin(file, mapping, pos,
29652963
len, flags, pagep, fsdata);
@@ -5005,17 +5003,14 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
50055003
}
50065004
if (IS_ENCRYPTED(inode)) {
50075005
inode->i_op = &ext4_encrypted_symlink_inode_operations;
5008-
ext4_set_aops(inode);
50095006
} else if (ext4_inode_is_fast_symlink(inode)) {
50105007
inode->i_link = (char *)ei->i_data;
50115008
inode->i_op = &ext4_fast_symlink_inode_operations;
50125009
nd_terminate_link(ei->i_data, inode->i_size,
50135010
sizeof(ei->i_data) - 1);
50145011
} else {
50155012
inode->i_op = &ext4_symlink_inode_operations;
5016-
ext4_set_aops(inode);
50175013
}
5018-
inode_nohighmem(inode);
50195014
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
50205015
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
50215016
inode->i_op = &ext4_special_inode_operations;

fs/ext4/namei.c

Lines changed: 55 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3249,6 +3249,32 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
32493249
return retval;
32503250
}
32513251

3252+
static int ext4_init_symlink_block(handle_t *handle, struct inode *inode,
3253+
struct fscrypt_str *disk_link)
3254+
{
3255+
struct buffer_head *bh;
3256+
char *kaddr;
3257+
int err = 0;
3258+
3259+
bh = ext4_bread(handle, inode, 0, EXT4_GET_BLOCKS_CREATE);
3260+
if (IS_ERR(bh))
3261+
return PTR_ERR(bh);
3262+
3263+
BUFFER_TRACE(bh, "get_write_access");
3264+
err = ext4_journal_get_write_access(handle, inode->i_sb, bh, EXT4_JTR_NONE);
3265+
if (err)
3266+
goto out;
3267+
3268+
kaddr = (char *)bh->b_data;
3269+
memcpy(kaddr, disk_link->name, disk_link->len);
3270+
inode->i_size = disk_link->len - 1;
3271+
EXT4_I(inode)->i_disksize = inode->i_size;
3272+
err = ext4_handle_dirty_metadata(handle, inode, bh);
3273+
out:
3274+
brelse(bh);
3275+
return err;
3276+
}
3277+
32523278
static int ext4_symlink(struct user_namespace *mnt_userns, struct inode *dir,
32533279
struct dentry *dentry, const char *symname)
32543280
{
@@ -3257,6 +3283,7 @@ static int ext4_symlink(struct user_namespace *mnt_userns, struct inode *dir,
32573283
int err, len = strlen(symname);
32583284
int credits;
32593285
struct fscrypt_str disk_link;
3286+
int retries = 0;
32603287

32613288
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
32623289
return -EIO;
@@ -3270,109 +3297,69 @@ static int ext4_symlink(struct user_namespace *mnt_userns, struct inode *dir,
32703297
if (err)
32713298
return err;
32723299

3273-
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
3274-
/*
3275-
* For non-fast symlinks, we just allocate inode and put it on
3276-
* orphan list in the first transaction => we need bitmap,
3277-
* group descriptor, sb, inode block, quota blocks, and
3278-
* possibly selinux xattr blocks.
3279-
*/
3280-
credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) +
3281-
EXT4_XATTR_TRANS_BLOCKS;
3282-
} else {
3283-
/*
3284-
* Fast symlink. We have to add entry to directory
3285-
* (EXT4_DATA_TRANS_BLOCKS + EXT4_INDEX_EXTRA_TRANS_BLOCKS),
3286-
* allocate new inode (bitmap, group descriptor, inode block,
3287-
* quota blocks, sb is already counted in previous macros).
3288-
*/
3289-
credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
3290-
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
3291-
}
3292-
3300+
/*
3301+
* EXT4_INDEX_EXTRA_TRANS_BLOCKS for addition of entry into the
3302+
* directory. +3 for inode, inode bitmap, group descriptor allocation.
3303+
* EXT4_DATA_TRANS_BLOCKS for the data block allocation and
3304+
* modification.
3305+
*/
3306+
credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
3307+
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
3308+
retry:
32933309
inode = ext4_new_inode_start_handle(mnt_userns, dir, S_IFLNK|S_IRWXUGO,
32943310
&dentry->d_name, 0, NULL,
32953311
EXT4_HT_DIR, credits);
32963312
handle = ext4_journal_current_handle();
32973313
if (IS_ERR(inode)) {
32983314
if (handle)
32993315
ext4_journal_stop(handle);
3300-
return PTR_ERR(inode);
3316+
err = PTR_ERR(inode);
3317+
goto out_retry;
33013318
}
33023319

33033320
if (IS_ENCRYPTED(inode)) {
33043321
err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link);
33053322
if (err)
33063323
goto err_drop_inode;
33073324
inode->i_op = &ext4_encrypted_symlink_inode_operations;
3325+
} else {
3326+
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
3327+
inode->i_op = &ext4_symlink_inode_operations;
3328+
} else {
3329+
inode->i_op = &ext4_fast_symlink_inode_operations;
3330+
inode->i_link = (char *)&EXT4_I(inode)->i_data;
3331+
}
33083332
}
33093333

33103334
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
3311-
if (!IS_ENCRYPTED(inode))
3312-
inode->i_op = &ext4_symlink_inode_operations;
3313-
inode_nohighmem(inode);
3314-
ext4_set_aops(inode);
3315-
/*
3316-
* We cannot call page_symlink() with transaction started
3317-
* because it calls into ext4_write_begin() which can wait
3318-
* for transaction commit if we are running out of space
3319-
* and thus we deadlock. So we have to stop transaction now
3320-
* and restart it when symlink contents is written.
3321-
*
3322-
* To keep fs consistent in case of crash, we have to put inode
3323-
* to orphan list in the mean time.
3324-
*/
3325-
drop_nlink(inode);
3326-
err = ext4_orphan_add(handle, inode);
3327-
if (handle)
3328-
ext4_journal_stop(handle);
3329-
handle = NULL;
3330-
if (err)
3331-
goto err_drop_inode;
3332-
err = __page_symlink(inode, disk_link.name, disk_link.len, 1);
3333-
if (err)
3334-
goto err_drop_inode;
3335-
/*
3336-
* Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS
3337-
* + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified
3338-
*/
3339-
handle = ext4_journal_start(dir, EXT4_HT_DIR,
3340-
EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
3341-
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1);
3342-
if (IS_ERR(handle)) {
3343-
err = PTR_ERR(handle);
3344-
handle = NULL;
3345-
goto err_drop_inode;
3346-
}
3347-
set_nlink(inode, 1);
3348-
err = ext4_orphan_del(handle, inode);
3335+
/* alloc symlink block and fill it */
3336+
err = ext4_init_symlink_block(handle, inode, &disk_link);
33493337
if (err)
33503338
goto err_drop_inode;
33513339
} else {
33523340
/* clear the extent format for fast symlink */
33533341
ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
3354-
if (!IS_ENCRYPTED(inode)) {
3355-
inode->i_op = &ext4_fast_symlink_inode_operations;
3356-
inode->i_link = (char *)&EXT4_I(inode)->i_data;
3357-
}
33583342
memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name,
33593343
disk_link.len);
33603344
inode->i_size = disk_link.len - 1;
3345+
EXT4_I(inode)->i_disksize = inode->i_size;
33613346
}
3362-
EXT4_I(inode)->i_disksize = inode->i_size;
33633347
err = ext4_add_nondir(handle, dentry, &inode);
33643348
if (handle)
33653349
ext4_journal_stop(handle);
33663350
iput(inode);
3367-
goto out_free_encrypted_link;
3351+
goto out_retry;
33683352

33693353
err_drop_inode:
3370-
if (handle)
3371-
ext4_journal_stop(handle);
33723354
clear_nlink(inode);
3355+
ext4_orphan_add(handle, inode);
33733356
unlock_new_inode(inode);
3357+
if (handle)
3358+
ext4_journal_stop(handle);
33743359
iput(inode);
3375-
out_free_encrypted_link:
3360+
out_retry:
3361+
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
3362+
goto retry;
33763363
if (disk_link.name != (unsigned char *)symname)
33773364
kfree(disk_link.name);
33783365
return err;

fs/ext4/symlink.c

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry,
2727
struct inode *inode,
2828
struct delayed_call *done)
2929
{
30-
struct page *cpage = NULL;
30+
struct buffer_head *bh = NULL;
3131
const void *caddr;
3232
unsigned int max_size;
3333
const char *paddr;
@@ -39,16 +39,19 @@ static const char *ext4_encrypted_get_link(struct dentry *dentry,
3939
caddr = EXT4_I(inode)->i_data;
4040
max_size = sizeof(EXT4_I(inode)->i_data);
4141
} else {
42-
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
43-
if (IS_ERR(cpage))
44-
return ERR_CAST(cpage);
45-
caddr = page_address(cpage);
42+
bh = ext4_bread(NULL, inode, 0, 0);
43+
if (IS_ERR(bh))
44+
return ERR_CAST(bh);
45+
if (!bh) {
46+
EXT4_ERROR_INODE(inode, "bad symlink.");
47+
return ERR_PTR(-EFSCORRUPTED);
48+
}
49+
caddr = bh->b_data;
4650
max_size = inode->i_sb->s_blocksize;
4751
}
4852

4953
paddr = fscrypt_get_symlink(inode, caddr, max_size, done);
50-
if (cpage)
51-
put_page(cpage);
54+
brelse(bh);
5255
return paddr;
5356
}
5457

@@ -62,6 +65,38 @@ static int ext4_encrypted_symlink_getattr(struct user_namespace *mnt_userns,
6265
return fscrypt_symlink_getattr(path, stat);
6366
}
6467

68+
static void ext4_free_link(void *bh)
69+
{
70+
brelse(bh);
71+
}
72+
73+
static const char *ext4_get_link(struct dentry *dentry, struct inode *inode,
74+
struct delayed_call *callback)
75+
{
76+
struct buffer_head *bh;
77+
78+
if (!dentry) {
79+
bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT);
80+
if (IS_ERR(bh))
81+
return ERR_CAST(bh);
82+
if (!bh || !ext4_buffer_uptodate(bh))
83+
return ERR_PTR(-ECHILD);
84+
} else {
85+
bh = ext4_bread(NULL, inode, 0, 0);
86+
if (IS_ERR(bh))
87+
return ERR_CAST(bh);
88+
if (!bh) {
89+
EXT4_ERROR_INODE(inode, "bad symlink.");
90+
return ERR_PTR(-EFSCORRUPTED);
91+
}
92+
}
93+
94+
set_delayed_call(callback, ext4_free_link, bh);
95+
nd_terminate_link(bh->b_data, inode->i_size,
96+
inode->i_sb->s_blocksize - 1);
97+
return bh->b_data;
98+
}
99+
65100
const struct inode_operations ext4_encrypted_symlink_inode_operations = {
66101
.get_link = ext4_encrypted_get_link,
67102
.setattr = ext4_setattr,
@@ -70,7 +105,7 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {
70105
};
71106

72107
const struct inode_operations ext4_symlink_inode_operations = {
73-
.get_link = page_get_link,
108+
.get_link = ext4_get_link,
74109
.setattr = ext4_setattr,
75110
.getattr = ext4_getattr,
76111
.listxattr = ext4_listxattr,

0 commit comments

Comments
 (0)