diff options
author | Theodore Ts'o <tytso@mit.edu> | 2015-04-16 01:55:00 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-04-16 01:55:00 -0400 |
commit | f348c252320b98e11176074fe04223f22bddaf0d (patch) | |
tree | 71fe0a1c86bd14f661a7e823e3b330adfc2719e3 /fs/ext4/namei.c | |
parent | 4461471107b79bee16c497c9f7f69fa26126ae5b (diff) |
ext4 crypto: add symlink encryption
Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/namei.c')
-rw-r--r-- | fs/ext4/namei.c | 85 |
1 files changed, 65 insertions, 20 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4c84db862891..d201426b8d39 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -3193,16 +3193,24 @@ static int ext4_symlink(struct inode *dir, | |||
3193 | { | 3193 | { |
3194 | handle_t *handle; | 3194 | handle_t *handle; |
3195 | struct inode *inode; | 3195 | struct inode *inode; |
3196 | int l, err, retries = 0; | 3196 | int err, len = strlen(symname); |
3197 | int credits; | 3197 | int credits; |
3198 | bool encryption_required; | ||
3199 | struct ext4_str disk_link; | ||
3200 | struct ext4_encrypted_symlink_data *sd = NULL; | ||
3198 | 3201 | ||
3199 | l = strlen(symname)+1; | 3202 | disk_link.len = len + 1; |
3200 | if (l > dir->i_sb->s_blocksize) | 3203 | disk_link.name = (char *) symname; |
3204 | |||
3205 | encryption_required = ext4_encrypted_inode(dir); | ||
3206 | if (encryption_required) | ||
3207 | disk_link.len = encrypted_symlink_data_len(len) + 1; | ||
3208 | if (disk_link.len > dir->i_sb->s_blocksize) | ||
3201 | return -ENAMETOOLONG; | 3209 | return -ENAMETOOLONG; |
3202 | 3210 | ||
3203 | dquot_initialize(dir); | 3211 | dquot_initialize(dir); |
3204 | 3212 | ||
3205 | if (l > EXT4_N_BLOCKS * 4) { | 3213 | if ((disk_link.len > EXT4_N_BLOCKS * 4)) { |
3206 | /* | 3214 | /* |
3207 | * For non-fast symlinks, we just allocate inode and put it on | 3215 | * For non-fast symlinks, we just allocate inode and put it on |
3208 | * orphan list in the first transaction => we need bitmap, | 3216 | * orphan list in the first transaction => we need bitmap, |
@@ -3221,16 +3229,49 @@ static int ext4_symlink(struct inode *dir, | |||
3221 | credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + | 3229 | credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + |
3222 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; | 3230 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; |
3223 | } | 3231 | } |
3224 | retry: | 3232 | |
3225 | inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, | 3233 | inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, |
3226 | &dentry->d_name, 0, NULL, | 3234 | &dentry->d_name, 0, NULL, |
3227 | EXT4_HT_DIR, credits); | 3235 | EXT4_HT_DIR, credits); |
3228 | handle = ext4_journal_current_handle(); | 3236 | handle = ext4_journal_current_handle(); |
3229 | err = PTR_ERR(inode); | 3237 | if (IS_ERR(inode)) { |
3230 | if (IS_ERR(inode)) | 3238 | if (handle) |
3231 | goto out_stop; | 3239 | ext4_journal_stop(handle); |
3240 | return PTR_ERR(inode); | ||
3241 | } | ||
3242 | |||
3243 | if (encryption_required) { | ||
3244 | struct ext4_fname_crypto_ctx *ctx = NULL; | ||
3245 | struct qstr istr; | ||
3246 | struct ext4_str ostr; | ||
3247 | |||
3248 | sd = kzalloc(disk_link.len, GFP_NOFS); | ||
3249 | if (!sd) { | ||
3250 | err = -ENOMEM; | ||
3251 | goto err_drop_inode; | ||
3252 | } | ||
3253 | err = ext4_inherit_context(dir, inode); | ||
3254 | if (err) | ||
3255 | goto err_drop_inode; | ||
3256 | ctx = ext4_get_fname_crypto_ctx(inode, | ||
3257 | inode->i_sb->s_blocksize); | ||
3258 | if (IS_ERR_OR_NULL(ctx)) { | ||
3259 | /* We just set the policy, so ctx should not be NULL */ | ||
3260 | err = (ctx == NULL) ? -EIO : PTR_ERR(ctx); | ||
3261 | goto err_drop_inode; | ||
3262 | } | ||
3263 | istr.name = (const unsigned char *) symname; | ||
3264 | istr.len = len; | ||
3265 | ostr.name = sd->encrypted_path; | ||
3266 | err = ext4_fname_usr_to_disk(ctx, &istr, &ostr); | ||
3267 | ext4_put_fname_crypto_ctx(&ctx); | ||
3268 | if (err < 0) | ||
3269 | goto err_drop_inode; | ||
3270 | sd->len = cpu_to_le16(ostr.len); | ||
3271 | disk_link.name = (char *) sd; | ||
3272 | } | ||
3232 | 3273 | ||
3233 | if (l > EXT4_N_BLOCKS * 4) { | 3274 | if ((disk_link.len > EXT4_N_BLOCKS * 4)) { |
3234 | inode->i_op = &ext4_symlink_inode_operations; | 3275 | inode->i_op = &ext4_symlink_inode_operations; |
3235 | ext4_set_aops(inode); | 3276 | ext4_set_aops(inode); |
3236 | /* | 3277 | /* |
@@ -3246,9 +3287,10 @@ retry: | |||
3246 | drop_nlink(inode); | 3287 | drop_nlink(inode); |
3247 | err = ext4_orphan_add(handle, inode); | 3288 | err = ext4_orphan_add(handle, inode); |
3248 | ext4_journal_stop(handle); | 3289 | ext4_journal_stop(handle); |
3290 | handle = NULL; | ||
3249 | if (err) | 3291 | if (err) |
3250 | goto err_drop_inode; | 3292 | goto err_drop_inode; |
3251 | err = __page_symlink(inode, symname, l, 1); | 3293 | err = __page_symlink(inode, disk_link.name, disk_link.len, 1); |
3252 | if (err) | 3294 | if (err) |
3253 | goto err_drop_inode; | 3295 | goto err_drop_inode; |
3254 | /* | 3296 | /* |
@@ -3260,34 +3302,37 @@ retry: | |||
3260 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); | 3302 | EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); |
3261 | if (IS_ERR(handle)) { | 3303 | if (IS_ERR(handle)) { |
3262 | err = PTR_ERR(handle); | 3304 | err = PTR_ERR(handle); |
3305 | handle = NULL; | ||
3263 | goto err_drop_inode; | 3306 | goto err_drop_inode; |
3264 | } | 3307 | } |
3265 | set_nlink(inode, 1); | 3308 | set_nlink(inode, 1); |
3266 | err = ext4_orphan_del(handle, inode); | 3309 | err = ext4_orphan_del(handle, inode); |
3267 | if (err) { | 3310 | if (err) |
3268 | ext4_journal_stop(handle); | ||
3269 | clear_nlink(inode); | ||
3270 | goto err_drop_inode; | 3311 | goto err_drop_inode; |
3271 | } | ||
3272 | } else { | 3312 | } else { |
3273 | /* clear the extent format for fast symlink */ | 3313 | /* clear the extent format for fast symlink */ |
3274 | ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); | 3314 | ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); |
3275 | inode->i_op = &ext4_fast_symlink_inode_operations; | 3315 | inode->i_op = encryption_required ? |
3276 | memcpy((char *)&EXT4_I(inode)->i_data, symname, l); | 3316 | &ext4_symlink_inode_operations : |
3277 | inode->i_size = l-1; | 3317 | &ext4_fast_symlink_inode_operations; |
3318 | memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, | ||
3319 | disk_link.len); | ||
3320 | inode->i_size = disk_link.len - 1; | ||
3278 | } | 3321 | } |
3279 | EXT4_I(inode)->i_disksize = inode->i_size; | 3322 | EXT4_I(inode)->i_disksize = inode->i_size; |
3280 | err = ext4_add_nondir(handle, dentry, inode); | 3323 | err = ext4_add_nondir(handle, dentry, inode); |
3281 | if (!err && IS_DIRSYNC(dir)) | 3324 | if (!err && IS_DIRSYNC(dir)) |
3282 | ext4_handle_sync(handle); | 3325 | ext4_handle_sync(handle); |
3283 | 3326 | ||
3284 | out_stop: | ||
3285 | if (handle) | 3327 | if (handle) |
3286 | ext4_journal_stop(handle); | 3328 | ext4_journal_stop(handle); |
3287 | if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) | 3329 | kfree(sd); |
3288 | goto retry; | ||
3289 | return err; | 3330 | return err; |
3290 | err_drop_inode: | 3331 | err_drop_inode: |
3332 | if (handle) | ||
3333 | ext4_journal_stop(handle); | ||
3334 | kfree(sd); | ||
3335 | clear_nlink(inode); | ||
3291 | unlock_new_inode(inode); | 3336 | unlock_new_inode(inode); |
3292 | iput(inode); | 3337 | iput(inode); |
3293 | return err; | 3338 | return err; |