diff options
author | Christoph Hellwig <hch@lst.de> | 2011-07-08 08:34:23 -0400 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2011-07-08 08:34:23 -0400 |
commit | c4ed4243c40f97ed5b7b121777bbbc6aeaa722f0 (patch) | |
tree | fda6054305f357705687016906a1ca254018c2e7 /fs/xfs | |
parent | dec58f1dfd30a3c3e9dadc808692f4e5cd922745 (diff) |
xfs: split xfs_setattr
Split up xfs_setattr into two functions, one for the complex truncate
handling, and one for the trivial attribute updates. Also move both
new routines to xfs_iops.c as they are fairly Linux-specific.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Alex Elder <aelder@sgi.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/linux-2.6/xfs_acl.c | 2 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_file.c | 2 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_iops.c | 440 | ||||
-rw-r--r-- | fs/xfs/xfs_vnodeops.c | 426 | ||||
-rw-r--r-- | fs/xfs/xfs_vnodeops.h | 3 |
5 files changed, 444 insertions, 429 deletions
diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c index 39f4f809bb68..115ac6919533 100644 --- a/fs/xfs/linux-2.6/xfs_acl.c +++ b/fs/xfs/linux-2.6/xfs_acl.c | |||
@@ -264,7 +264,7 @@ xfs_set_mode(struct inode *inode, mode_t mode) | |||
264 | iattr.ia_mode = mode; | 264 | iattr.ia_mode = mode; |
265 | iattr.ia_ctime = current_fs_time(inode->i_sb); | 265 | iattr.ia_ctime = current_fs_time(inode->i_sb); |
266 | 266 | ||
267 | error = -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_NOACL); | 267 | error = -xfs_setattr_nonsize(XFS_I(inode), &iattr, XFS_ATTR_NOACL); |
268 | } | 268 | } |
269 | 269 | ||
270 | return error; | 270 | return error; |
diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 7f782af286bf..8073f61efb8e 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c | |||
@@ -944,7 +944,7 @@ xfs_file_fallocate( | |||
944 | 944 | ||
945 | iattr.ia_valid = ATTR_SIZE; | 945 | iattr.ia_valid = ATTR_SIZE; |
946 | iattr.ia_size = new_size; | 946 | iattr.ia_size = new_size; |
947 | error = -xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK); | 947 | error = -xfs_setattr_size(ip, &iattr, XFS_ATTR_NOLOCK); |
948 | } | 948 | } |
949 | 949 | ||
950 | out_unlock: | 950 | out_unlock: |
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index d44d92cd12b1..65d3bf8266e7 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include "xfs_buf_item.h" | 39 | #include "xfs_buf_item.h" |
40 | #include "xfs_utils.h" | 40 | #include "xfs_utils.h" |
41 | #include "xfs_vnodeops.h" | 41 | #include "xfs_vnodeops.h" |
42 | #include "xfs_inode_item.h" | ||
42 | #include "xfs_trace.h" | 43 | #include "xfs_trace.h" |
43 | 44 | ||
44 | #include <linux/capability.h> | 45 | #include <linux/capability.h> |
@@ -497,12 +498,449 @@ xfs_vn_getattr( | |||
497 | return 0; | 498 | return 0; |
498 | } | 499 | } |
499 | 500 | ||
501 | int | ||
502 | xfs_setattr_nonsize( | ||
503 | struct xfs_inode *ip, | ||
504 | struct iattr *iattr, | ||
505 | int flags) | ||
506 | { | ||
507 | xfs_mount_t *mp = ip->i_mount; | ||
508 | struct inode *inode = VFS_I(ip); | ||
509 | int mask = iattr->ia_valid; | ||
510 | xfs_trans_t *tp; | ||
511 | int error; | ||
512 | uid_t uid = 0, iuid = 0; | ||
513 | gid_t gid = 0, igid = 0; | ||
514 | struct xfs_dquot *udqp = NULL, *gdqp = NULL; | ||
515 | struct xfs_dquot *olddquot1 = NULL, *olddquot2 = NULL; | ||
516 | |||
517 | trace_xfs_setattr(ip); | ||
518 | |||
519 | if (mp->m_flags & XFS_MOUNT_RDONLY) | ||
520 | return XFS_ERROR(EROFS); | ||
521 | |||
522 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
523 | return XFS_ERROR(EIO); | ||
524 | |||
525 | error = -inode_change_ok(inode, iattr); | ||
526 | if (error) | ||
527 | return XFS_ERROR(error); | ||
528 | |||
529 | ASSERT((mask & ATTR_SIZE) == 0); | ||
530 | |||
531 | /* | ||
532 | * If disk quotas is on, we make sure that the dquots do exist on disk, | ||
533 | * before we start any other transactions. Trying to do this later | ||
534 | * is messy. We don't care to take a readlock to look at the ids | ||
535 | * in inode here, because we can't hold it across the trans_reserve. | ||
536 | * If the IDs do change before we take the ilock, we're covered | ||
537 | * because the i_*dquot fields will get updated anyway. | ||
538 | */ | ||
539 | if (XFS_IS_QUOTA_ON(mp) && (mask & (ATTR_UID|ATTR_GID))) { | ||
540 | uint qflags = 0; | ||
541 | |||
542 | if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) { | ||
543 | uid = iattr->ia_uid; | ||
544 | qflags |= XFS_QMOPT_UQUOTA; | ||
545 | } else { | ||
546 | uid = ip->i_d.di_uid; | ||
547 | } | ||
548 | if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) { | ||
549 | gid = iattr->ia_gid; | ||
550 | qflags |= XFS_QMOPT_GQUOTA; | ||
551 | } else { | ||
552 | gid = ip->i_d.di_gid; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * We take a reference when we initialize udqp and gdqp, | ||
557 | * so it is important that we never blindly double trip on | ||
558 | * the same variable. See xfs_create() for an example. | ||
559 | */ | ||
560 | ASSERT(udqp == NULL); | ||
561 | ASSERT(gdqp == NULL); | ||
562 | error = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip), | ||
563 | qflags, &udqp, &gdqp); | ||
564 | if (error) | ||
565 | return error; | ||
566 | } | ||
567 | |||
568 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); | ||
569 | error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0); | ||
570 | if (error) | ||
571 | goto out_dqrele; | ||
572 | |||
573 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
574 | |||
575 | /* | ||
576 | * Change file ownership. Must be the owner or privileged. | ||
577 | */ | ||
578 | if (mask & (ATTR_UID|ATTR_GID)) { | ||
579 | /* | ||
580 | * These IDs could have changed since we last looked at them. | ||
581 | * But, we're assured that if the ownership did change | ||
582 | * while we didn't have the inode locked, inode's dquot(s) | ||
583 | * would have changed also. | ||
584 | */ | ||
585 | iuid = ip->i_d.di_uid; | ||
586 | igid = ip->i_d.di_gid; | ||
587 | gid = (mask & ATTR_GID) ? iattr->ia_gid : igid; | ||
588 | uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid; | ||
589 | |||
590 | /* | ||
591 | * Do a quota reservation only if uid/gid is actually | ||
592 | * going to change. | ||
593 | */ | ||
594 | if (XFS_IS_QUOTA_RUNNING(mp) && | ||
595 | ((XFS_IS_UQUOTA_ON(mp) && iuid != uid) || | ||
596 | (XFS_IS_GQUOTA_ON(mp) && igid != gid))) { | ||
597 | ASSERT(tp); | ||
598 | error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, | ||
599 | capable(CAP_FOWNER) ? | ||
600 | XFS_QMOPT_FORCE_RES : 0); | ||
601 | if (error) /* out of quota */ | ||
602 | goto out_trans_cancel; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | xfs_trans_ijoin(tp, ip); | ||
607 | |||
608 | /* | ||
609 | * Change file ownership. Must be the owner or privileged. | ||
610 | */ | ||
611 | if (mask & (ATTR_UID|ATTR_GID)) { | ||
612 | /* | ||
613 | * CAP_FSETID overrides the following restrictions: | ||
614 | * | ||
615 | * The set-user-ID and set-group-ID bits of a file will be | ||
616 | * cleared upon successful return from chown() | ||
617 | */ | ||
618 | if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && | ||
619 | !capable(CAP_FSETID)) | ||
620 | ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); | ||
621 | |||
622 | /* | ||
623 | * Change the ownerships and register quota modifications | ||
624 | * in the transaction. | ||
625 | */ | ||
626 | if (iuid != uid) { | ||
627 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_UQUOTA_ON(mp)) { | ||
628 | ASSERT(mask & ATTR_UID); | ||
629 | ASSERT(udqp); | ||
630 | olddquot1 = xfs_qm_vop_chown(tp, ip, | ||
631 | &ip->i_udquot, udqp); | ||
632 | } | ||
633 | ip->i_d.di_uid = uid; | ||
634 | inode->i_uid = uid; | ||
635 | } | ||
636 | if (igid != gid) { | ||
637 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_GQUOTA_ON(mp)) { | ||
638 | ASSERT(!XFS_IS_PQUOTA_ON(mp)); | ||
639 | ASSERT(mask & ATTR_GID); | ||
640 | ASSERT(gdqp); | ||
641 | olddquot2 = xfs_qm_vop_chown(tp, ip, | ||
642 | &ip->i_gdquot, gdqp); | ||
643 | } | ||
644 | ip->i_d.di_gid = gid; | ||
645 | inode->i_gid = gid; | ||
646 | } | ||
647 | } | ||
648 | |||
649 | /* | ||
650 | * Change file access modes. | ||
651 | */ | ||
652 | if (mask & ATTR_MODE) { | ||
653 | umode_t mode = iattr->ia_mode; | ||
654 | |||
655 | if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) | ||
656 | mode &= ~S_ISGID; | ||
657 | |||
658 | ip->i_d.di_mode &= S_IFMT; | ||
659 | ip->i_d.di_mode |= mode & ~S_IFMT; | ||
660 | |||
661 | inode->i_mode &= S_IFMT; | ||
662 | inode->i_mode |= mode & ~S_IFMT; | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * Change file access or modified times. | ||
667 | */ | ||
668 | if (mask & ATTR_ATIME) { | ||
669 | inode->i_atime = iattr->ia_atime; | ||
670 | ip->i_d.di_atime.t_sec = iattr->ia_atime.tv_sec; | ||
671 | ip->i_d.di_atime.t_nsec = iattr->ia_atime.tv_nsec; | ||
672 | ip->i_update_core = 1; | ||
673 | } | ||
674 | if (mask & ATTR_CTIME) { | ||
675 | inode->i_ctime = iattr->ia_ctime; | ||
676 | ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec; | ||
677 | ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec; | ||
678 | ip->i_update_core = 1; | ||
679 | } | ||
680 | if (mask & ATTR_MTIME) { | ||
681 | inode->i_mtime = iattr->ia_mtime; | ||
682 | ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec; | ||
683 | ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec; | ||
684 | ip->i_update_core = 1; | ||
685 | } | ||
686 | |||
687 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
688 | |||
689 | XFS_STATS_INC(xs_ig_attrchg); | ||
690 | |||
691 | if (mp->m_flags & XFS_MOUNT_WSYNC) | ||
692 | xfs_trans_set_sync(tp); | ||
693 | error = xfs_trans_commit(tp, 0); | ||
694 | |||
695 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
696 | |||
697 | /* | ||
698 | * Release any dquot(s) the inode had kept before chown. | ||
699 | */ | ||
700 | xfs_qm_dqrele(olddquot1); | ||
701 | xfs_qm_dqrele(olddquot2); | ||
702 | xfs_qm_dqrele(udqp); | ||
703 | xfs_qm_dqrele(gdqp); | ||
704 | |||
705 | if (error) | ||
706 | return XFS_ERROR(error); | ||
707 | |||
708 | /* | ||
709 | * XXX(hch): Updating the ACL entries is not atomic vs the i_mode | ||
710 | * update. We could avoid this with linked transactions | ||
711 | * and passing down the transaction pointer all the way | ||
712 | * to attr_set. No previous user of the generic | ||
713 | * Posix ACL code seems to care about this issue either. | ||
714 | */ | ||
715 | if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) { | ||
716 | error = -xfs_acl_chmod(inode); | ||
717 | if (error) | ||
718 | return XFS_ERROR(error); | ||
719 | } | ||
720 | |||
721 | return 0; | ||
722 | |||
723 | out_trans_cancel: | ||
724 | xfs_trans_cancel(tp, 0); | ||
725 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
726 | out_dqrele: | ||
727 | xfs_qm_dqrele(udqp); | ||
728 | xfs_qm_dqrele(gdqp); | ||
729 | return error; | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * Truncate file. Must have write permission and not be a directory. | ||
734 | */ | ||
735 | int | ||
736 | xfs_setattr_size( | ||
737 | struct xfs_inode *ip, | ||
738 | struct iattr *iattr, | ||
739 | int flags) | ||
740 | { | ||
741 | struct xfs_mount *mp = ip->i_mount; | ||
742 | struct inode *inode = VFS_I(ip); | ||
743 | int mask = iattr->ia_valid; | ||
744 | struct xfs_trans *tp; | ||
745 | int error; | ||
746 | uint lock_flags; | ||
747 | uint commit_flags = 0; | ||
748 | |||
749 | trace_xfs_setattr(ip); | ||
750 | |||
751 | if (mp->m_flags & XFS_MOUNT_RDONLY) | ||
752 | return XFS_ERROR(EROFS); | ||
753 | |||
754 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
755 | return XFS_ERROR(EIO); | ||
756 | |||
757 | error = -inode_change_ok(inode, iattr); | ||
758 | if (error) | ||
759 | return XFS_ERROR(error); | ||
760 | |||
761 | ASSERT(S_ISREG(ip->i_d.di_mode)); | ||
762 | ASSERT((mask & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_ATIME_SET| | ||
763 | ATTR_MTIME_SET|ATTR_KILL_SUID|ATTR_KILL_SGID| | ||
764 | ATTR_KILL_PRIV|ATTR_TIMES_SET)) == 0); | ||
765 | |||
766 | lock_flags = XFS_ILOCK_EXCL; | ||
767 | if (!(flags & XFS_ATTR_NOLOCK)) | ||
768 | lock_flags |= XFS_IOLOCK_EXCL; | ||
769 | xfs_ilock(ip, lock_flags); | ||
770 | |||
771 | /* | ||
772 | * Short circuit the truncate case for zero length files. | ||
773 | */ | ||
774 | if (iattr->ia_size == 0 && | ||
775 | ip->i_size == 0 && ip->i_d.di_nextents == 0) { | ||
776 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
777 | lock_flags &= ~XFS_ILOCK_EXCL; | ||
778 | if (mask & ATTR_CTIME) { | ||
779 | inode->i_mtime = inode->i_ctime = | ||
780 | current_fs_time(inode->i_sb); | ||
781 | xfs_mark_inode_dirty_sync(ip); | ||
782 | } | ||
783 | goto out_unlock; | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * Make sure that the dquots are attached to the inode. | ||
788 | */ | ||
789 | error = xfs_qm_dqattach_locked(ip, 0); | ||
790 | if (error) | ||
791 | goto out_unlock; | ||
792 | |||
793 | /* | ||
794 | * Now we can make the changes. Before we join the inode to the | ||
795 | * transaction, take care of the part of the truncation that must be | ||
796 | * done without the inode lock. This needs to be done before joining | ||
797 | * the inode to the transaction, because the inode cannot be unlocked | ||
798 | * once it is a part of the transaction. | ||
799 | */ | ||
800 | if (iattr->ia_size > ip->i_size) { | ||
801 | /* | ||
802 | * Do the first part of growing a file: zero any data in the | ||
803 | * last block that is beyond the old EOF. We need to do this | ||
804 | * before the inode is joined to the transaction to modify | ||
805 | * i_size. | ||
806 | */ | ||
807 | error = xfs_zero_eof(ip, iattr->ia_size, ip->i_size); | ||
808 | if (error) | ||
809 | goto out_unlock; | ||
810 | } | ||
811 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
812 | lock_flags &= ~XFS_ILOCK_EXCL; | ||
813 | |||
814 | /* | ||
815 | * We are going to log the inode size change in this transaction so | ||
816 | * any previous writes that are beyond the on disk EOF and the new | ||
817 | * EOF that have not been written out need to be written here. If we | ||
818 | * do not write the data out, we expose ourselves to the null files | ||
819 | * problem. | ||
820 | * | ||
821 | * Only flush from the on disk size to the smaller of the in memory | ||
822 | * file size or the new size as that's the range we really care about | ||
823 | * here and prevents waiting for other data not within the range we | ||
824 | * care about here. | ||
825 | */ | ||
826 | if (ip->i_size != ip->i_d.di_size && iattr->ia_size > ip->i_d.di_size) { | ||
827 | error = xfs_flush_pages(ip, ip->i_d.di_size, iattr->ia_size, | ||
828 | XBF_ASYNC, FI_NONE); | ||
829 | if (error) | ||
830 | goto out_unlock; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Wait for all I/O to complete. | ||
835 | */ | ||
836 | xfs_ioend_wait(ip); | ||
837 | |||
838 | error = -block_truncate_page(inode->i_mapping, iattr->ia_size, | ||
839 | xfs_get_blocks); | ||
840 | if (error) | ||
841 | goto out_unlock; | ||
842 | |||
843 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); | ||
844 | error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, | ||
845 | XFS_TRANS_PERM_LOG_RES, | ||
846 | XFS_ITRUNCATE_LOG_COUNT); | ||
847 | if (error) | ||
848 | goto out_trans_cancel; | ||
849 | |||
850 | truncate_setsize(inode, iattr->ia_size); | ||
851 | |||
852 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; | ||
853 | lock_flags |= XFS_ILOCK_EXCL; | ||
854 | |||
855 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
856 | |||
857 | xfs_trans_ijoin(tp, ip); | ||
858 | |||
859 | /* | ||
860 | * Only change the c/mtime if we are changing the size or we are | ||
861 | * explicitly asked to change it. This handles the semantic difference | ||
862 | * between truncate() and ftruncate() as implemented in the VFS. | ||
863 | * | ||
864 | * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a | ||
865 | * special case where we need to update the times despite not having | ||
866 | * these flags set. For all other operations the VFS set these flags | ||
867 | * explicitly if it wants a timestamp update. | ||
868 | */ | ||
869 | if (iattr->ia_size != ip->i_size && | ||
870 | (!(mask & (ATTR_CTIME | ATTR_MTIME)))) { | ||
871 | iattr->ia_ctime = iattr->ia_mtime = | ||
872 | current_fs_time(inode->i_sb); | ||
873 | mask |= ATTR_CTIME | ATTR_MTIME; | ||
874 | } | ||
875 | |||
876 | if (iattr->ia_size > ip->i_size) { | ||
877 | ip->i_d.di_size = iattr->ia_size; | ||
878 | ip->i_size = iattr->ia_size; | ||
879 | } else if (iattr->ia_size <= ip->i_size || | ||
880 | (iattr->ia_size == 0 && ip->i_d.di_nextents)) { | ||
881 | /* | ||
882 | * Signal a sync transaction unless we are truncating an | ||
883 | * already unlinked file on a wsync filesystem. | ||
884 | */ | ||
885 | error = xfs_itruncate_finish(&tp, ip, iattr->ia_size, | ||
886 | XFS_DATA_FORK, | ||
887 | ((ip->i_d.di_nlink != 0 || | ||
888 | !(mp->m_flags & XFS_MOUNT_WSYNC)) | ||
889 | ? 1 : 0)); | ||
890 | if (error) | ||
891 | goto out_trans_abort; | ||
892 | |||
893 | /* | ||
894 | * Truncated "down", so we're removing references to old data | ||
895 | * here - if we delay flushing for a long time, we expose | ||
896 | * ourselves unduly to the notorious NULL files problem. So, | ||
897 | * we mark this inode and flush it when the file is closed, | ||
898 | * and do not wait the usual (long) time for writeout. | ||
899 | */ | ||
900 | xfs_iflags_set(ip, XFS_ITRUNCATED); | ||
901 | } | ||
902 | |||
903 | if (mask & ATTR_CTIME) { | ||
904 | inode->i_ctime = iattr->ia_ctime; | ||
905 | ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec; | ||
906 | ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec; | ||
907 | ip->i_update_core = 1; | ||
908 | } | ||
909 | if (mask & ATTR_MTIME) { | ||
910 | inode->i_mtime = iattr->ia_mtime; | ||
911 | ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec; | ||
912 | ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec; | ||
913 | ip->i_update_core = 1; | ||
914 | } | ||
915 | |||
916 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
917 | |||
918 | XFS_STATS_INC(xs_ig_attrchg); | ||
919 | |||
920 | if (mp->m_flags & XFS_MOUNT_WSYNC) | ||
921 | xfs_trans_set_sync(tp); | ||
922 | |||
923 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | ||
924 | out_unlock: | ||
925 | if (lock_flags) | ||
926 | xfs_iunlock(ip, lock_flags); | ||
927 | return error; | ||
928 | |||
929 | out_trans_abort: | ||
930 | commit_flags |= XFS_TRANS_ABORT; | ||
931 | out_trans_cancel: | ||
932 | xfs_trans_cancel(tp, commit_flags); | ||
933 | goto out_unlock; | ||
934 | } | ||
935 | |||
500 | STATIC int | 936 | STATIC int |
501 | xfs_vn_setattr( | 937 | xfs_vn_setattr( |
502 | struct dentry *dentry, | 938 | struct dentry *dentry, |
503 | struct iattr *iattr) | 939 | struct iattr *iattr) |
504 | { | 940 | { |
505 | return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0); | 941 | if (iattr->ia_valid & ATTR_SIZE) |
942 | return -xfs_setattr_size(XFS_I(dentry->d_inode), iattr, 0); | ||
943 | return -xfs_setattr_nonsize(XFS_I(dentry->d_inode), iattr, 0); | ||
506 | } | 944 | } |
507 | 945 | ||
508 | #define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR) | 946 | #define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR) |
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 619720705bc6..a4f56a42ef90 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c | |||
@@ -50,430 +50,6 @@ | |||
50 | #include "xfs_vnodeops.h" | 50 | #include "xfs_vnodeops.h" |
51 | #include "xfs_trace.h" | 51 | #include "xfs_trace.h" |
52 | 52 | ||
53 | int | ||
54 | xfs_setattr( | ||
55 | struct xfs_inode *ip, | ||
56 | struct iattr *iattr, | ||
57 | int flags) | ||
58 | { | ||
59 | xfs_mount_t *mp = ip->i_mount; | ||
60 | struct inode *inode = VFS_I(ip); | ||
61 | int mask = iattr->ia_valid; | ||
62 | xfs_trans_t *tp; | ||
63 | int code; | ||
64 | uint lock_flags; | ||
65 | uint commit_flags=0; | ||
66 | uid_t uid=0, iuid=0; | ||
67 | gid_t gid=0, igid=0; | ||
68 | struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; | ||
69 | int need_iolock = 1; | ||
70 | |||
71 | trace_xfs_setattr(ip); | ||
72 | |||
73 | if (mp->m_flags & XFS_MOUNT_RDONLY) | ||
74 | return XFS_ERROR(EROFS); | ||
75 | |||
76 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
77 | return XFS_ERROR(EIO); | ||
78 | |||
79 | code = -inode_change_ok(inode, iattr); | ||
80 | if (code) | ||
81 | return code; | ||
82 | |||
83 | olddquot1 = olddquot2 = NULL; | ||
84 | udqp = gdqp = NULL; | ||
85 | |||
86 | /* | ||
87 | * If disk quotas is on, we make sure that the dquots do exist on disk, | ||
88 | * before we start any other transactions. Trying to do this later | ||
89 | * is messy. We don't care to take a readlock to look at the ids | ||
90 | * in inode here, because we can't hold it across the trans_reserve. | ||
91 | * If the IDs do change before we take the ilock, we're covered | ||
92 | * because the i_*dquot fields will get updated anyway. | ||
93 | */ | ||
94 | if (XFS_IS_QUOTA_ON(mp) && (mask & (ATTR_UID|ATTR_GID))) { | ||
95 | uint qflags = 0; | ||
96 | |||
97 | if ((mask & ATTR_UID) && XFS_IS_UQUOTA_ON(mp)) { | ||
98 | uid = iattr->ia_uid; | ||
99 | qflags |= XFS_QMOPT_UQUOTA; | ||
100 | } else { | ||
101 | uid = ip->i_d.di_uid; | ||
102 | } | ||
103 | if ((mask & ATTR_GID) && XFS_IS_GQUOTA_ON(mp)) { | ||
104 | gid = iattr->ia_gid; | ||
105 | qflags |= XFS_QMOPT_GQUOTA; | ||
106 | } else { | ||
107 | gid = ip->i_d.di_gid; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * We take a reference when we initialize udqp and gdqp, | ||
112 | * so it is important that we never blindly double trip on | ||
113 | * the same variable. See xfs_create() for an example. | ||
114 | */ | ||
115 | ASSERT(udqp == NULL); | ||
116 | ASSERT(gdqp == NULL); | ||
117 | code = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip), | ||
118 | qflags, &udqp, &gdqp); | ||
119 | if (code) | ||
120 | return code; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * For the other attributes, we acquire the inode lock and | ||
125 | * first do an error checking pass. | ||
126 | */ | ||
127 | tp = NULL; | ||
128 | lock_flags = XFS_ILOCK_EXCL; | ||
129 | if (flags & XFS_ATTR_NOLOCK) | ||
130 | need_iolock = 0; | ||
131 | if (!(mask & ATTR_SIZE)) { | ||
132 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); | ||
133 | commit_flags = 0; | ||
134 | code = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), | ||
135 | 0, 0, 0); | ||
136 | if (code) { | ||
137 | lock_flags = 0; | ||
138 | goto error_return; | ||
139 | } | ||
140 | } else { | ||
141 | if (need_iolock) | ||
142 | lock_flags |= XFS_IOLOCK_EXCL; | ||
143 | } | ||
144 | |||
145 | xfs_ilock(ip, lock_flags); | ||
146 | |||
147 | /* | ||
148 | * Change file ownership. Must be the owner or privileged. | ||
149 | */ | ||
150 | if (mask & (ATTR_UID|ATTR_GID)) { | ||
151 | /* | ||
152 | * These IDs could have changed since we last looked at them. | ||
153 | * But, we're assured that if the ownership did change | ||
154 | * while we didn't have the inode locked, inode's dquot(s) | ||
155 | * would have changed also. | ||
156 | */ | ||
157 | iuid = ip->i_d.di_uid; | ||
158 | igid = ip->i_d.di_gid; | ||
159 | gid = (mask & ATTR_GID) ? iattr->ia_gid : igid; | ||
160 | uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid; | ||
161 | |||
162 | /* | ||
163 | * Do a quota reservation only if uid/gid is actually | ||
164 | * going to change. | ||
165 | */ | ||
166 | if (XFS_IS_QUOTA_RUNNING(mp) && | ||
167 | ((XFS_IS_UQUOTA_ON(mp) && iuid != uid) || | ||
168 | (XFS_IS_GQUOTA_ON(mp) && igid != gid))) { | ||
169 | ASSERT(tp); | ||
170 | code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, | ||
171 | capable(CAP_FOWNER) ? | ||
172 | XFS_QMOPT_FORCE_RES : 0); | ||
173 | if (code) /* out of quota */ | ||
174 | goto error_return; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Truncate file. Must have write permission and not be a directory. | ||
180 | */ | ||
181 | if (mask & ATTR_SIZE) { | ||
182 | /* Short circuit the truncate case for zero length files */ | ||
183 | if (iattr->ia_size == 0 && | ||
184 | ip->i_size == 0 && ip->i_d.di_nextents == 0) { | ||
185 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
186 | lock_flags &= ~XFS_ILOCK_EXCL; | ||
187 | if (mask & ATTR_CTIME) { | ||
188 | inode->i_mtime = inode->i_ctime = | ||
189 | current_fs_time(inode->i_sb); | ||
190 | xfs_mark_inode_dirty_sync(ip); | ||
191 | } | ||
192 | code = 0; | ||
193 | goto error_return; | ||
194 | } | ||
195 | |||
196 | if (S_ISDIR(ip->i_d.di_mode)) { | ||
197 | code = XFS_ERROR(EISDIR); | ||
198 | goto error_return; | ||
199 | } else if (!S_ISREG(ip->i_d.di_mode)) { | ||
200 | code = XFS_ERROR(EINVAL); | ||
201 | goto error_return; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Make sure that the dquots are attached to the inode. | ||
206 | */ | ||
207 | code = xfs_qm_dqattach_locked(ip, 0); | ||
208 | if (code) | ||
209 | goto error_return; | ||
210 | |||
211 | /* | ||
212 | * Now we can make the changes. Before we join the inode | ||
213 | * to the transaction, if ATTR_SIZE is set then take care of | ||
214 | * the part of the truncation that must be done without the | ||
215 | * inode lock. This needs to be done before joining the inode | ||
216 | * to the transaction, because the inode cannot be unlocked | ||
217 | * once it is a part of the transaction. | ||
218 | */ | ||
219 | if (iattr->ia_size > ip->i_size) { | ||
220 | /* | ||
221 | * Do the first part of growing a file: zero any data | ||
222 | * in the last block that is beyond the old EOF. We | ||
223 | * need to do this before the inode is joined to the | ||
224 | * transaction to modify the i_size. | ||
225 | */ | ||
226 | code = xfs_zero_eof(ip, iattr->ia_size, ip->i_size); | ||
227 | if (code) | ||
228 | goto error_return; | ||
229 | } | ||
230 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
231 | lock_flags &= ~XFS_ILOCK_EXCL; | ||
232 | |||
233 | /* | ||
234 | * We are going to log the inode size change in this | ||
235 | * transaction so any previous writes that are beyond the on | ||
236 | * disk EOF and the new EOF that have not been written out need | ||
237 | * to be written here. If we do not write the data out, we | ||
238 | * expose ourselves to the null files problem. | ||
239 | * | ||
240 | * Only flush from the on disk size to the smaller of the in | ||
241 | * memory file size or the new size as that's the range we | ||
242 | * really care about here and prevents waiting for other data | ||
243 | * not within the range we care about here. | ||
244 | */ | ||
245 | if (ip->i_size != ip->i_d.di_size && | ||
246 | iattr->ia_size > ip->i_d.di_size) { | ||
247 | code = xfs_flush_pages(ip, | ||
248 | ip->i_d.di_size, iattr->ia_size, | ||
249 | XBF_ASYNC, FI_NONE); | ||
250 | if (code) | ||
251 | goto error_return; | ||
252 | } | ||
253 | |||
254 | /* wait for all I/O to complete */ | ||
255 | xfs_ioend_wait(ip); | ||
256 | |||
257 | code = -block_truncate_page(inode->i_mapping, iattr->ia_size, | ||
258 | xfs_get_blocks); | ||
259 | if (code) | ||
260 | goto error_return; | ||
261 | |||
262 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); | ||
263 | code = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, | ||
264 | XFS_TRANS_PERM_LOG_RES, | ||
265 | XFS_ITRUNCATE_LOG_COUNT); | ||
266 | if (code) | ||
267 | goto error_return; | ||
268 | |||
269 | truncate_setsize(inode, iattr->ia_size); | ||
270 | |||
271 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; | ||
272 | lock_flags |= XFS_ILOCK_EXCL; | ||
273 | |||
274 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
275 | |||
276 | xfs_trans_ijoin(tp, ip); | ||
277 | |||
278 | /* | ||
279 | * Only change the c/mtime if we are changing the size | ||
280 | * or we are explicitly asked to change it. This handles | ||
281 | * the semantic difference between truncate() and ftruncate() | ||
282 | * as implemented in the VFS. | ||
283 | * | ||
284 | * The regular truncate() case without ATTR_CTIME and ATTR_MTIME | ||
285 | * is a special case where we need to update the times despite | ||
286 | * not having these flags set. For all other operations the | ||
287 | * VFS set these flags explicitly if it wants a timestamp | ||
288 | * update. | ||
289 | */ | ||
290 | if (iattr->ia_size != ip->i_size && | ||
291 | (!(mask & (ATTR_CTIME | ATTR_MTIME)))) { | ||
292 | iattr->ia_ctime = iattr->ia_mtime = | ||
293 | current_fs_time(inode->i_sb); | ||
294 | mask |= ATTR_CTIME | ATTR_MTIME; | ||
295 | } | ||
296 | |||
297 | if (iattr->ia_size > ip->i_size) { | ||
298 | ip->i_d.di_size = iattr->ia_size; | ||
299 | ip->i_size = iattr->ia_size; | ||
300 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
301 | } else if (iattr->ia_size <= ip->i_size || | ||
302 | (iattr->ia_size == 0 && ip->i_d.di_nextents)) { | ||
303 | /* | ||
304 | * signal a sync transaction unless | ||
305 | * we're truncating an already unlinked | ||
306 | * file on a wsync filesystem | ||
307 | */ | ||
308 | code = xfs_itruncate_finish(&tp, ip, iattr->ia_size, | ||
309 | XFS_DATA_FORK, | ||
310 | ((ip->i_d.di_nlink != 0 || | ||
311 | !(mp->m_flags & XFS_MOUNT_WSYNC)) | ||
312 | ? 1 : 0)); | ||
313 | if (code) | ||
314 | goto abort_return; | ||
315 | /* | ||
316 | * Truncated "down", so we're removing references | ||
317 | * to old data here - if we now delay flushing for | ||
318 | * a long time, we expose ourselves unduly to the | ||
319 | * notorious NULL files problem. So, we mark this | ||
320 | * vnode and flush it when the file is closed, and | ||
321 | * do not wait the usual (long) time for writeout. | ||
322 | */ | ||
323 | xfs_iflags_set(ip, XFS_ITRUNCATED); | ||
324 | } | ||
325 | } else if (tp) { | ||
326 | xfs_trans_ijoin(tp, ip); | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Change file ownership. Must be the owner or privileged. | ||
331 | */ | ||
332 | if (mask & (ATTR_UID|ATTR_GID)) { | ||
333 | /* | ||
334 | * CAP_FSETID overrides the following restrictions: | ||
335 | * | ||
336 | * The set-user-ID and set-group-ID bits of a file will be | ||
337 | * cleared upon successful return from chown() | ||
338 | */ | ||
339 | if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && | ||
340 | !capable(CAP_FSETID)) { | ||
341 | ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * Change the ownerships and register quota modifications | ||
346 | * in the transaction. | ||
347 | */ | ||
348 | if (iuid != uid) { | ||
349 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_UQUOTA_ON(mp)) { | ||
350 | ASSERT(mask & ATTR_UID); | ||
351 | ASSERT(udqp); | ||
352 | olddquot1 = xfs_qm_vop_chown(tp, ip, | ||
353 | &ip->i_udquot, udqp); | ||
354 | } | ||
355 | ip->i_d.di_uid = uid; | ||
356 | inode->i_uid = uid; | ||
357 | } | ||
358 | if (igid != gid) { | ||
359 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_GQUOTA_ON(mp)) { | ||
360 | ASSERT(!XFS_IS_PQUOTA_ON(mp)); | ||
361 | ASSERT(mask & ATTR_GID); | ||
362 | ASSERT(gdqp); | ||
363 | olddquot2 = xfs_qm_vop_chown(tp, ip, | ||
364 | &ip->i_gdquot, gdqp); | ||
365 | } | ||
366 | ip->i_d.di_gid = gid; | ||
367 | inode->i_gid = gid; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * Change file access modes. | ||
373 | */ | ||
374 | if (mask & ATTR_MODE) { | ||
375 | umode_t mode = iattr->ia_mode; | ||
376 | |||
377 | if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) | ||
378 | mode &= ~S_ISGID; | ||
379 | |||
380 | ip->i_d.di_mode &= S_IFMT; | ||
381 | ip->i_d.di_mode |= mode & ~S_IFMT; | ||
382 | |||
383 | inode->i_mode &= S_IFMT; | ||
384 | inode->i_mode |= mode & ~S_IFMT; | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Change file access or modified times. | ||
389 | */ | ||
390 | if (mask & ATTR_ATIME) { | ||
391 | inode->i_atime = iattr->ia_atime; | ||
392 | ip->i_d.di_atime.t_sec = iattr->ia_atime.tv_sec; | ||
393 | ip->i_d.di_atime.t_nsec = iattr->ia_atime.tv_nsec; | ||
394 | ip->i_update_core = 1; | ||
395 | } | ||
396 | if (mask & ATTR_CTIME) { | ||
397 | inode->i_ctime = iattr->ia_ctime; | ||
398 | ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec; | ||
399 | ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec; | ||
400 | ip->i_update_core = 1; | ||
401 | } | ||
402 | if (mask & ATTR_MTIME) { | ||
403 | inode->i_mtime = iattr->ia_mtime; | ||
404 | ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec; | ||
405 | ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec; | ||
406 | ip->i_update_core = 1; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * And finally, log the inode core if any attribute in it | ||
411 | * has been changed. | ||
412 | */ | ||
413 | if (mask & (ATTR_UID|ATTR_GID|ATTR_MODE| | ||
414 | ATTR_ATIME|ATTR_CTIME|ATTR_MTIME)) | ||
415 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | ||
416 | |||
417 | XFS_STATS_INC(xs_ig_attrchg); | ||
418 | |||
419 | /* | ||
420 | * If this is a synchronous mount, make sure that the | ||
421 | * transaction goes to disk before returning to the user. | ||
422 | * This is slightly sub-optimal in that truncates require | ||
423 | * two sync transactions instead of one for wsync filesystems. | ||
424 | * One for the truncate and one for the timestamps since we | ||
425 | * don't want to change the timestamps unless we're sure the | ||
426 | * truncate worked. Truncates are less than 1% of the laddis | ||
427 | * mix so this probably isn't worth the trouble to optimize. | ||
428 | */ | ||
429 | code = 0; | ||
430 | if (mp->m_flags & XFS_MOUNT_WSYNC) | ||
431 | xfs_trans_set_sync(tp); | ||
432 | |||
433 | code = xfs_trans_commit(tp, commit_flags); | ||
434 | |||
435 | xfs_iunlock(ip, lock_flags); | ||
436 | |||
437 | /* | ||
438 | * Release any dquot(s) the inode had kept before chown. | ||
439 | */ | ||
440 | xfs_qm_dqrele(olddquot1); | ||
441 | xfs_qm_dqrele(olddquot2); | ||
442 | xfs_qm_dqrele(udqp); | ||
443 | xfs_qm_dqrele(gdqp); | ||
444 | |||
445 | if (code) | ||
446 | return code; | ||
447 | |||
448 | /* | ||
449 | * XXX(hch): Updating the ACL entries is not atomic vs the i_mode | ||
450 | * update. We could avoid this with linked transactions | ||
451 | * and passing down the transaction pointer all the way | ||
452 | * to attr_set. No previous user of the generic | ||
453 | * Posix ACL code seems to care about this issue either. | ||
454 | */ | ||
455 | if ((mask & ATTR_MODE) && !(flags & XFS_ATTR_NOACL)) { | ||
456 | code = -xfs_acl_chmod(inode); | ||
457 | if (code) | ||
458 | return XFS_ERROR(code); | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | |||
463 | abort_return: | ||
464 | commit_flags |= XFS_TRANS_ABORT; | ||
465 | error_return: | ||
466 | xfs_qm_dqrele(udqp); | ||
467 | xfs_qm_dqrele(gdqp); | ||
468 | if (tp) { | ||
469 | xfs_trans_cancel(tp, commit_flags); | ||
470 | } | ||
471 | if (lock_flags != 0) { | ||
472 | xfs_iunlock(ip, lock_flags); | ||
473 | } | ||
474 | return code; | ||
475 | } | ||
476 | |||
477 | /* | 53 | /* |
478 | * The maximum pathlen is 1024 bytes. Since the minimum file system | 54 | * The maximum pathlen is 1024 bytes. Since the minimum file system |
479 | * blocksize is 512 bytes, we can get a max of 2 extents back from | 55 | * blocksize is 512 bytes, we can get a max of 2 extents back from |
@@ -2784,7 +2360,7 @@ xfs_change_file_space( | |||
2784 | iattr.ia_valid = ATTR_SIZE; | 2360 | iattr.ia_valid = ATTR_SIZE; |
2785 | iattr.ia_size = startoffset; | 2361 | iattr.ia_size = startoffset; |
2786 | 2362 | ||
2787 | error = xfs_setattr(ip, &iattr, attr_flags); | 2363 | error = xfs_setattr_size(ip, &iattr, attr_flags); |
2788 | 2364 | ||
2789 | if (error) | 2365 | if (error) |
2790 | return error; | 2366 | return error; |
diff --git a/fs/xfs/xfs_vnodeops.h b/fs/xfs/xfs_vnodeops.h index 3bcd23353d6c..35d3d513e1e9 100644 --- a/fs/xfs/xfs_vnodeops.h +++ b/fs/xfs/xfs_vnodeops.h | |||
@@ -13,7 +13,8 @@ struct xfs_inode; | |||
13 | struct xfs_iomap; | 13 | struct xfs_iomap; |
14 | 14 | ||
15 | 15 | ||
16 | int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags); | 16 | int xfs_setattr_nonsize(struct xfs_inode *ip, struct iattr *vap, int flags); |
17 | int xfs_setattr_size(struct xfs_inode *ip, struct iattr *vap, int flags); | ||
17 | #define XFS_ATTR_DMI 0x01 /* invocation from a DMI function */ | 18 | #define XFS_ATTR_DMI 0x01 /* invocation from a DMI function */ |
18 | #define XFS_ATTR_NONBLOCK 0x02 /* return EAGAIN if operation would block */ | 19 | #define XFS_ATTR_NONBLOCK 0x02 /* return EAGAIN if operation would block */ |
19 | #define XFS_ATTR_NOLOCK 0x04 /* Don't grab any conflicting locks */ | 20 | #define XFS_ATTR_NOLOCK 0x04 /* Don't grab any conflicting locks */ |