aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2011-07-08 08:34:23 -0400
committerChristoph Hellwig <hch@lst.de>2011-07-08 08:34:23 -0400
commitc4ed4243c40f97ed5b7b121777bbbc6aeaa722f0 (patch)
treefda6054305f357705687016906a1ca254018c2e7 /fs/xfs
parentdec58f1dfd30a3c3e9dadc808692f4e5cd922745 (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.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_file.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.c440
-rw-r--r--fs/xfs/xfs_vnodeops.c426
-rw-r--r--fs/xfs/xfs_vnodeops.h3
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
950out_unlock: 950out_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
501int
502xfs_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
723out_trans_cancel:
724 xfs_trans_cancel(tp, 0);
725 xfs_iunlock(ip, XFS_ILOCK_EXCL);
726out_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 */
735int
736xfs_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);
924out_unlock:
925 if (lock_flags)
926 xfs_iunlock(ip, lock_flags);
927 return error;
928
929out_trans_abort:
930 commit_flags |= XFS_TRANS_ABORT;
931out_trans_cancel:
932 xfs_trans_cancel(tp, commit_flags);
933 goto out_unlock;
934}
935
500STATIC int 936STATIC int
501xfs_vn_setattr( 937xfs_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
53int
54xfs_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;
13struct xfs_iomap; 13struct xfs_iomap;
14 14
15 15
16int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags); 16int xfs_setattr_nonsize(struct xfs_inode *ip, struct iattr *vap, int flags);
17int 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 */