diff options
author | Tao Ma <tao.ma@oracle.com> | 2009-08-17 23:40:59 -0400 |
---|---|---|
committer | Joel Becker <joel.becker@oracle.com> | 2009-09-22 23:09:39 -0400 |
commit | a9063ab9a3827483007124bdb6f9877f0ab4c3f5 (patch) | |
tree | 78afcc391daf9d66df5bf34b207aa8e9c53ca7d6 /fs/ocfs2 | |
parent | 110a045aca62f6f564e3b68f89af2a3a5a6ecff2 (diff) |
ocfs2: handle file attributes issue for reflink.
A reflink creates a snapshot of a file, that means the attributes
must be identical except for three exceptions - nlink, ino, and ctime.
As for time changes, Here is a brief description:
1. Source file:
1) atime: Ignore. Let the lazy atime code handle that.
2) mtime: don't touch.
3) ctime: If we change the tree (adding REFCOUNTED to at least one
extent), update it.
2. Destination file:
1) atime: ignore.
2) mtime: we want it to appear identical to the source.
3) ctime: update.
The idea here is that an ls -l will show the same time for the
src and target - it shows mtime. Backup software like rsync and tar
will treat the new file correctly too.
Signed-off-by: Tao Ma <tao.ma@oracle.com>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/refcounttree.c | 122 |
1 files changed, 120 insertions, 2 deletions
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index e3171c483685..62d21c6ce1d9 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c | |||
@@ -3350,10 +3350,44 @@ out: | |||
3350 | return ret; | 3350 | return ret; |
3351 | } | 3351 | } |
3352 | 3352 | ||
3353 | static int ocfs2_change_ctime(struct inode *inode, | ||
3354 | struct buffer_head *di_bh) | ||
3355 | { | ||
3356 | int ret; | ||
3357 | handle_t *handle; | ||
3358 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
3359 | |||
3360 | handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), | ||
3361 | OCFS2_INODE_UPDATE_CREDITS); | ||
3362 | if (IS_ERR(handle)) { | ||
3363 | ret = PTR_ERR(handle); | ||
3364 | mlog_errno(ret); | ||
3365 | goto out; | ||
3366 | } | ||
3367 | |||
3368 | ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh, | ||
3369 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
3370 | if (ret) { | ||
3371 | mlog_errno(ret); | ||
3372 | goto out_commit; | ||
3373 | } | ||
3374 | |||
3375 | inode->i_ctime = CURRENT_TIME; | ||
3376 | di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); | ||
3377 | di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); | ||
3378 | |||
3379 | ocfs2_journal_dirty(handle, di_bh); | ||
3380 | |||
3381 | out_commit: | ||
3382 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
3383 | out: | ||
3384 | return ret; | ||
3385 | } | ||
3386 | |||
3353 | static int ocfs2_attach_refcount_tree(struct inode *inode, | 3387 | static int ocfs2_attach_refcount_tree(struct inode *inode, |
3354 | struct buffer_head *di_bh) | 3388 | struct buffer_head *di_bh) |
3355 | { | 3389 | { |
3356 | int ret; | 3390 | int ret, data_changed = 0; |
3357 | struct buffer_head *ref_root_bh = NULL; | 3391 | struct buffer_head *ref_root_bh = NULL; |
3358 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | 3392 | struct ocfs2_inode_info *oi = OCFS2_I(inode); |
3359 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | 3393 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; |
@@ -3402,12 +3436,21 @@ static int ocfs2_attach_refcount_tree(struct inode *inode, | |||
3402 | &dealloc); | 3436 | &dealloc); |
3403 | if (ret) { | 3437 | if (ret) { |
3404 | mlog_errno(ret); | 3438 | mlog_errno(ret); |
3405 | break; | 3439 | goto unlock; |
3406 | } | 3440 | } |
3441 | |||
3442 | data_changed = 1; | ||
3407 | } | 3443 | } |
3408 | cpos += num_clusters; | 3444 | cpos += num_clusters; |
3409 | } | 3445 | } |
3410 | 3446 | ||
3447 | if (data_changed) { | ||
3448 | ret = ocfs2_change_ctime(inode, di_bh); | ||
3449 | if (ret) | ||
3450 | mlog_errno(ret); | ||
3451 | } | ||
3452 | |||
3453 | unlock: | ||
3411 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | 3454 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); |
3412 | brelse(ref_root_bh); | 3455 | brelse(ref_root_bh); |
3413 | 3456 | ||
@@ -3522,6 +3565,74 @@ out: | |||
3522 | return ret; | 3565 | return ret; |
3523 | } | 3566 | } |
3524 | 3567 | ||
3568 | /* | ||
3569 | * change the new file's attributes to the src. | ||
3570 | * | ||
3571 | * reflink creates a snapshot of a file, that means the attributes | ||
3572 | * must be identical except for three exceptions - nlink, ino, and ctime. | ||
3573 | */ | ||
3574 | static int ocfs2_complete_reflink(struct inode *s_inode, | ||
3575 | struct buffer_head *s_bh, | ||
3576 | struct inode *t_inode, | ||
3577 | struct buffer_head *t_bh) | ||
3578 | { | ||
3579 | int ret; | ||
3580 | handle_t *handle; | ||
3581 | struct ocfs2_dinode *s_di = (struct ocfs2_dinode *)s_bh->b_data; | ||
3582 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)t_bh->b_data; | ||
3583 | loff_t size = i_size_read(s_inode); | ||
3584 | |||
3585 | handle = ocfs2_start_trans(OCFS2_SB(t_inode->i_sb), | ||
3586 | OCFS2_INODE_UPDATE_CREDITS); | ||
3587 | if (IS_ERR(handle)) { | ||
3588 | ret = PTR_ERR(handle); | ||
3589 | mlog_errno(ret); | ||
3590 | return ret; | ||
3591 | } | ||
3592 | |||
3593 | ret = ocfs2_journal_access_di(handle, INODE_CACHE(t_inode), t_bh, | ||
3594 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
3595 | if (ret) { | ||
3596 | mlog_errno(ret); | ||
3597 | goto out_commit; | ||
3598 | } | ||
3599 | |||
3600 | spin_lock(&OCFS2_I(t_inode)->ip_lock); | ||
3601 | OCFS2_I(t_inode)->ip_clusters = OCFS2_I(s_inode)->ip_clusters; | ||
3602 | OCFS2_I(t_inode)->ip_attr = OCFS2_I(s_inode)->ip_attr; | ||
3603 | OCFS2_I(t_inode)->ip_dyn_features = OCFS2_I(s_inode)->ip_dyn_features; | ||
3604 | spin_unlock(&OCFS2_I(t_inode)->ip_lock); | ||
3605 | i_size_write(t_inode, size); | ||
3606 | |||
3607 | di->i_xattr_inline_size = s_di->i_xattr_inline_size; | ||
3608 | di->i_clusters = s_di->i_clusters; | ||
3609 | di->i_size = s_di->i_size; | ||
3610 | di->i_dyn_features = s_di->i_dyn_features; | ||
3611 | di->i_attr = s_di->i_attr; | ||
3612 | di->i_uid = s_di->i_uid; | ||
3613 | di->i_gid = s_di->i_gid; | ||
3614 | di->i_mode = s_di->i_mode; | ||
3615 | |||
3616 | /* | ||
3617 | * update time. | ||
3618 | * we want mtime to appear identical to the source and update ctime. | ||
3619 | */ | ||
3620 | t_inode->i_ctime = CURRENT_TIME; | ||
3621 | |||
3622 | di->i_ctime = cpu_to_le64(t_inode->i_ctime.tv_sec); | ||
3623 | di->i_ctime_nsec = cpu_to_le32(t_inode->i_ctime.tv_nsec); | ||
3624 | |||
3625 | t_inode->i_mtime = s_inode->i_mtime; | ||
3626 | di->i_mtime = s_di->i_mtime; | ||
3627 | di->i_mtime_nsec = s_di->i_mtime_nsec; | ||
3628 | |||
3629 | ocfs2_journal_dirty(handle, t_bh); | ||
3630 | |||
3631 | out_commit: | ||
3632 | ocfs2_commit_trans(OCFS2_SB(t_inode->i_sb), handle); | ||
3633 | return ret; | ||
3634 | } | ||
3635 | |||
3525 | static int ocfs2_create_reflink_node(struct inode *s_inode, | 3636 | static int ocfs2_create_reflink_node(struct inode *s_inode, |
3526 | struct buffer_head *s_bh, | 3637 | struct buffer_head *s_bh, |
3527 | struct inode *t_inode, | 3638 | struct inode *t_inode, |
@@ -3555,9 +3666,16 @@ static int ocfs2_create_reflink_node(struct inode *s_inode, | |||
3555 | ret = ocfs2_duplicate_extent_list(s_inode, t_inode, t_bh, | 3666 | ret = ocfs2_duplicate_extent_list(s_inode, t_inode, t_bh, |
3556 | &ref_tree->rf_ci, ref_root_bh, | 3667 | &ref_tree->rf_ci, ref_root_bh, |
3557 | &dealloc); | 3668 | &dealloc); |
3669 | if (ret) { | ||
3670 | mlog_errno(ret); | ||
3671 | goto out_unlock_refcount; | ||
3672 | } | ||
3673 | |||
3674 | ret = ocfs2_complete_reflink(s_inode, s_bh, t_inode, t_bh); | ||
3558 | if (ret) | 3675 | if (ret) |
3559 | mlog_errno(ret); | 3676 | mlog_errno(ret); |
3560 | 3677 | ||
3678 | out_unlock_refcount: | ||
3561 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); | 3679 | ocfs2_unlock_refcount_tree(osb, ref_tree, 1); |
3562 | brelse(ref_root_bh); | 3680 | brelse(ref_root_bh); |
3563 | out: | 3681 | out: |